Skip to content

Commit

Permalink
adds initial mini-game impl - spawning character for players upon joi…
Browse files Browse the repository at this point in the history
…n + allows basic movement
  • Loading branch information
l-brendle committed Oct 19, 2023
1 parent d82f2cf commit 8de0220
Show file tree
Hide file tree
Showing 12 changed files with 289 additions and 30 deletions.
1 change: 1 addition & 0 deletions angular-frontend/src/app/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
<div *ngIf="client.connected && inventories" class="columns">
<div class="column is-9">
<app-inventory *ngFor="let inventory of inventories" [inventory]="inventory" (itemCreated)="onItemCreated($event, inventory)"></app-inventory>
<app-game [client]="client" [currentUser]="currentUser"></app-game>
</div>
<div class="column is-3">
<app-chat [client]="client" [currentUser]="currentUser"></app-chat>
Expand Down
25 changes: 5 additions & 20 deletions angular-frontend/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export class AppComponent implements OnDestroy {
this.client.subscribe("/app/inventory", (payload => this.updateInventory(JSON.parse(payload.body))));
this.client.subscribe("/topic/inventory", (payload => this.updateInventory(JSON.parse(payload.body))));
this.client.subscribe("/topic/game/character", (payload => this.updateCharacter(JSON.parse(payload.body))))
this.client.subscribe("/topic/game/character_removal", (payload => this.removeCharacter(JSON.parse(payload.body))))
};

this.client.onWebSocketError = (error) => {
Expand Down Expand Up @@ -74,26 +75,6 @@ export class AppComponent implements OnDestroy {
}
}

@HostListener('window:keyup', ['$event'])
keyEvent(event: KeyboardEvent) {
if(this.client.connected){
const keyCode = event.code
console.log("KeyEvent: " + keyCode);

if(keyCode === "KeyJ"){
console.log("Joining Game:");
const payload = "joining_game"
this.client?.publish({ destination: "/app/match_join", body: JSON.stringify(payload)});
} else if(keyCode === "ArrowLeft"
|| keyCode === "ArrowRight"
|| keyCode === "ArrowDown"
|| keyCode === "ArrowUp") {
this.client?.publish({destination: "/app/game_control", body: JSON.stringify(keyCode)})
}
}

}

updateCursors(newPosition: Cursor) {
if (this.currentUser === newPosition.name) {
return
Expand Down Expand Up @@ -175,4 +156,8 @@ export class AppComponent implements OnDestroy {
this.characters[characterIndexToUpdate] = character;
}
}

removeCharacter(character : Character) {
this.characters = this.characters.filter(char => char.name != character.name)
}
}
3 changes: 2 additions & 1 deletion angular-frontend/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ import { InventoryItemComponent } from './components/inventory-item/inventory-it
import { ChatComponent } from './components/chat/chat.component';
import { StockComponent } from './components/stock/stock.component';
import { UserBarComponent } from './components/user-bar/user-bar.component';
import { GameComponent } from './components/game/game.component';
import { balIconAccount, balIconSend } from '@baloise/design-system-icons'

@NgModule({
declarations: [AppComponent, CheckInComponent, InventoryComponent, InventoryItemComponent, ChatComponent, StockComponent, UserBarComponent],
declarations: [AppComponent, CheckInComponent, InventoryComponent, InventoryItemComponent, ChatComponent, GameComponent, StockComponent, UserBarComponent],
imports: [
BrowserModule,
// Provide all components and value accessors to the app module.
Expand Down
7 changes: 7 additions & 0 deletions angular-frontend/src/app/components/game/game.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<bal-card class="mt-medium">
<bal-card-content class="is-flex fg-small">
<bal-heading level="medium">Mini-Game</bal-heading>
<bal-button (click)="joinGame()"><bal-icon name="check"></bal-icon></bal-button>
<bal-button (click)="leaveGame()"><bal-icon name="close"></bal-icon></bal-button>
</bal-card-content>
</bal-card>
Empty file.
74 changes: 74 additions & 0 deletions angular-frontend/src/app/components/game/game.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import {Component, HostListener, Input, OnDestroy, OnInit} from '@angular/core';

import { Client } from '@stomp/stompjs';

@Component({
selector: 'app-game',
templateUrl: './game.component.html',
styleUrls: ['./game.component.scss']
})
export class GameComponent implements OnInit, OnDestroy {

@Input() client!: Client;
@Input() currentUser!: String;

connected : boolean = false;

ngOnInit(): void {
if (this.client.connected) {
//this.client.subscribe("/topic/chat", payload => this.addMessage(payload));
}
}

ngOnDestroy(): void {
if (this.client.connected) {
//this.client.unsubscribe("/topic/chat")
}
}

joinGame() {
const payload = "joining_game";
this.client?.publish({ destination: "/app/game/match_join", body: JSON.stringify(payload)});
this.connected = true;
}

leaveGame() {
const payload = "leaving_game"
this.client?.publish({ destination: "/app/game/match_leave", body: JSON.stringify(payload)});
this.connected = false;
}

@HostListener('window:keyup', ['$event'])
keyUpEvent(event: KeyboardEvent) {
if(this.client.connected){
const keyCode = event.code
console.log("KeyEvent: " + keyCode);

if(keyCode === "ArrowLeft"
|| keyCode === "ArrowRight"
|| keyCode === "ArrowDown"
|| keyCode === "ArrowUp") {
const payload = {keyCode : keyCode, pressed : false}
this.client?.publish({destination: "/app/game/character_control", body: JSON.stringify(payload)})
}
}
}

@HostListener('window:keydown', ['$event'])
keyDownEvent(event: KeyboardEvent) {
if(this.client.connected){
const keyCode = event.code
console.log("KeyEvent: " + keyCode);

if(keyCode === "ArrowLeft"
|| keyCode === "ArrowRight"
|| keyCode === "ArrowDown"
|| keyCode === "ArrowUp") {
const payload = {keyCode : keyCode, pressed : true}
this.client?.publish({destination: "/app/game/character_control", body: JSON.stringify(payload)})
}
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling
@EnableAsync
public class SpringBackendApplication {

public static void main(String[] args) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package com.baloise.collab.springbackend.game;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.extern.java.Log;

import java.util.*;

@Data
@Builder
@Log
public class Character {

private String name;
private String color;
private long posX;
private long posY;
private Set<String> pressedKeys;

private final float speed = 0.1f;

public void addToPressedKeys(String keyCode) {
pressedKeys.add(keyCode);
}

public void removeFromPressedKeys(String keyCode) {
pressedKeys.remove(keyCode);
}

public int getPosXRounded() {
return Math.round(posX);
}

public int getPosYRounded() {
return Math.round(posY);
}

@Override
public boolean equals(Object o){
return o.getClass() == this.getClass()
&& Objects.equals(((Character) o).name, this.name);
}

@AllArgsConstructor
private static class MovementDirection {

public int dirX = 0;
public int dirY = 0;

public static MovementDirection add(MovementDirection a, MovementDirection b) {
return new MovementDirection(a.dirX + b.dirX, a.dirY + b.dirY);
}
}

public void move(Long frameTime) {
MovementDirection movementDirection;
if (pressedKeys != null && !pressedKeys.isEmpty()) {
var directions = pressedKeys.stream().map(this::getMovementDirforKey);
movementDirection = directions.reduce(new MovementDirection(0, 0), MovementDirection::add);
} else {
movementDirection = new MovementDirection(0, 0);
}
this.posX = Math.round(posX + movementDirection.dirX * frameTime * speed);
this.posY = Math.round(posY + movementDirection.dirY * frameTime * speed);
}

private MovementDirection getMovementDirforKey(String key) {
return switch (key) {
case "ArrowLeft" -> new MovementDirection(-1, 0);
case "ArrowRight" -> new MovementDirection(1, 0);
case "ArrowDown" -> new MovementDirection(0, 1);
case "ArrowUp" -> new MovementDirection(0, -1);
default -> new MovementDirection(0, 0);
};
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import com.baloise.collab.springbackend.useradmin.UserDTO;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.java.Log;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Component;

import java.util.HashSet;
Expand All @@ -11,38 +13,59 @@
import java.util.Set;

@Component
@RequiredArgsConstructor
@Log
public class CharacterController {

@Getter
private final Set<CharacterDTO> activeCharacters = new HashSet<>();
private final Set<Character> activeCharacters = new HashSet<>();

private final SimpMessagingTemplate messageSender;

private final int spawnX = 500;
private final int spawnY = 500;

public CharacterDTO createCharacterForUser(UserDTO user) {
var characterForUser = getActiveCharacterForName(user.name());
if (characterForUser.isEmpty()) {
var character = new CharacterDTO(user.name(), user.color(), spawnX, spawnY);
var character = Character.builder().name(user.name())
.color(user.color())
.posX(spawnX)
.posY(spawnY)
.pressedKeys(new HashSet<>())
.build();
activeCharacters.add(character);
return character;
return new CharacterDTO(character.getName(), character.getColor(), character.getPosXRounded(), character.getPosYRounded());
} else {
return characterForUser.get();
var character = characterForUser.get();
return new CharacterDTO(character.getName(), character.getColor(), character.getPosXRounded(), character.getPosYRounded());
}
}

public Optional<CharacterDTO> getActiveCharacterForName(String name) {
public Optional<Character> getActiveCharacterForName(String name) {
return activeCharacters.stream()
.filter(charDTO -> Objects.equals(charDTO.name(), name))
.filter(character -> Objects.equals(character.getName(), name))
.findFirst();
}

public void removeFromActiveCharacters(String name) {
var charToRemove = getActiveCharacterForName(name);
if (charToRemove.isPresent()) {
activeCharacters.remove(charToRemove.get());
log.info(name + " removed from active Users");
sendRemoveCharacter(charToRemove.get());
var result = activeCharacters.remove(charToRemove.get());
log.info(name + " removed from active Users: " + result);
}
}

public void moveCharacter(Character character, long frametime) {
character.move(frametime);
CharacterDTO characterDTO = new CharacterDTO(character.getName(), character.getColor(), character.getPosXRounded(), character.getPosYRounded());
messageSender.convertAndSend("/topic/game/character", characterDTO);
}

private void sendRemoveCharacter(Character character) {
CharacterDTO characterDTO = new CharacterDTO(character.getName(), character.getColor(), character.getPosXRounded(), character.getPosYRounded());
messageSender.convertAndSend("/topic/game/character_removal", characterDTO);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.baloise.collab.springbackend.game;


import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@RequiredArgsConstructor
@Service
public class GameLoopExecutor {

private final CharacterController characterController;
private Long lastFrameTime;

@Getter
private boolean running;

@Async
public void run() throws InterruptedException {
running = true;
while (running) {
var characters = characterController.getActiveCharacters();
var time = System.currentTimeMillis();
if(!characters.isEmpty()){
characters.forEach(character -> characterController.moveCharacter(character, time-lastFrameTime));
}
lastFrameTime = System.currentTimeMillis();
Thread.sleep(50);
}
}

@Async
public void quit(){
running = false;
lastFrameTime = 0L;
}



}
Loading

0 comments on commit 8de0220

Please sign in to comment.