diff --git a/front/dist/maps/map2.json b/front/dist/maps/map2.json index 9a10e634..d68f4df9 100644 --- a/front/dist/maps/map2.json +++ b/front/dist/maps/map2.json @@ -155,6 +155,12 @@ }, { "id":77, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }], "terrain":[2, 2, 2, 2] }, { diff --git a/front/dist/resources/objects/rockSprite.png b/front/dist/resources/objects/rockSprite.png new file mode 100644 index 00000000..40820abd Binary files /dev/null and b/front/dist/resources/objects/rockSprite.png differ diff --git a/front/src/Phaser/Entity/PlayableCaracter.ts b/front/src/Phaser/Entity/PlayableCaracter.ts new file mode 100644 index 00000000..66ddd579 --- /dev/null +++ b/front/src/Phaser/Entity/PlayableCaracter.ts @@ -0,0 +1,57 @@ +import {getPlayerAnimations, playAnimation, PlayerAnimationNames} from "../Player/Animation"; +import {ActiveEventList, UserInputEvent} from "../UserInput/UserInputManager"; +import {SpeechBubble} from "./SpeechBubble"; + +export class PlayableCaracter extends Phaser.Physics.Arcade.Sprite { + private bubble: SpeechBubble; + + constructor(scene: Phaser.Scene, x: number, y: number, texture: string, frame?: string | number) { + super(scene, x, y, texture, frame); + + this.scene.sys.updateList.add(this); + this.scene.sys.displayList.add(this); + this.setScale(2); + this.scene.physics.world.enableBody(this); + this.setImmovable(true); + this.setCollideWorldBounds(true); + this.setSize(32, 32); //edit the hitbox to better match the caracter model + } + + move(x: number, y: number){ + + this.setVelocity(x, y); + + //todo improve animations to better account for diagonal movement + if (this.body.velocity.x > 0) { //moving right + this.play(PlayerAnimationNames.WalkRight, true); + } + if (this.body.velocity.x < 0) { //moving left + this.anims.playReverse(PlayerAnimationNames.WalkLeft, true); + } + if (this.body.velocity.y < 0) { //moving up + this.play(PlayerAnimationNames.WalkUp, true); + } + if (this.body.velocity.y > 0) { //moving down + this.play(PlayerAnimationNames.WalkDown, true); + } + + if(this.bubble) { + this.bubble.moveBubble(this.x, this.y); + } + } + + stop(){ + this.setVelocity(0, 0); + this.play(PlayerAnimationNames.None, true); + } + + say(text: string) { + if (this.bubble) return; + this.bubble = new SpeechBubble(this.scene, this, text) + //todo make the buble destroy on player movement? + setTimeout(() => { + this.bubble.destroy(); + this.bubble = null; + }, 3000) + } +} \ No newline at end of file diff --git a/front/src/Phaser/Entity/SpeechBubble.ts b/front/src/Phaser/Entity/SpeechBubble.ts new file mode 100644 index 00000000..51aaa169 --- /dev/null +++ b/front/src/Phaser/Entity/SpeechBubble.ts @@ -0,0 +1,88 @@ +import Scene = Phaser.Scene; +import {PlayableCaracter} from "./PlayableCaracter"; + +export class SpeechBubble { + private bubble: Phaser.GameObjects.Graphics; + private content: Phaser.GameObjects.Text; + + /** + * + * @param scene + * @param player + * @param text + */ + constructor(scene: Scene, player: PlayableCaracter, text: string = "") { + + let bubbleHeight = 50; + let bubblePadding = 10; + let bubbleWidth = bubblePadding * 2 + text.length * 10; + let arrowHeight = bubbleHeight / 4; + + this.bubble = scene.add.graphics({ x: player.x + 16, y: player.y - 80 }); + + // Bubble shadow + this.bubble.fillStyle(0x222222, 0.5); + this.bubble.fillRoundedRect(6, 6, bubbleWidth, bubbleHeight, 16); + + // this.bubble color + this.bubble.fillStyle(0xffffff, 1); + + // this.bubble outline line style + this.bubble.lineStyle(4, 0x565656, 1); + + // this.bubble shape and outline + this.bubble.strokeRoundedRect(0, 0, bubbleWidth, bubbleHeight, 16); + this.bubble.fillRoundedRect(0, 0, bubbleWidth, bubbleHeight, 16); + + // Calculate arrow coordinates + let point1X = Math.floor(bubbleWidth / 7); + let point1Y = bubbleHeight; + let point2X = Math.floor((bubbleWidth / 7) * 2); + let point2Y = bubbleHeight; + let point3X = Math.floor(bubbleWidth / 7); + let point3Y = Math.floor(bubbleHeight + arrowHeight); + + // bubble arrow shadow + this.bubble.lineStyle(4, 0x222222, 0.5); + this.bubble.lineBetween(point2X - 1, point2Y + 6, point3X + 2, point3Y); + + // bubble arrow fill + this.bubble.fillTriangle(point1X, point1Y, point2X, point2Y, point3X, point3Y); + this.bubble.lineStyle(2, 0x565656, 1); + this.bubble.lineBetween(point2X, point2Y, point3X, point3Y); + this.bubble.lineBetween(point1X, point1Y, point3X, point3Y); + + this.content = scene.add.text(0, 0, text, { fontFamily: 'Arial', fontSize: 20, color: '#000000', align: 'center', wordWrap: { width: bubbleWidth - (bubblePadding * 2) } }); + + let bounds = this.content.getBounds(); + this.content.setPosition(this.bubble.x + (bubbleWidth / 2) - (bounds.width / 2), this.bubble.y + (bubbleHeight / 2) - (bounds.height / 2)); + } + + /** + * + * @param x + * @param y + */ + moveBubble(x : number, y : number) { + if (this.bubble) { + this.bubble.setPosition((x + 16), (y - 80)); + } + if (this.content) { + let bubbleHeight = 50; + let bubblePadding = 10; + let bubbleWidth = bubblePadding * 2 + this.content.text.length * 10; + let bounds = this.content.getBounds(); + //this.content.setPosition(x, y); + this.content.setPosition(this.bubble.x + (bubbleWidth / 2) - (bounds.width / 2), this.bubble.y + (bubbleHeight / 2) - (bounds.height / 2)); + } + } + + destroy(): void { + this.bubble.setVisible(false) //todo find a better way + this.bubble.destroy(); + this.bubble = null; + this.content.destroy(); + this.content = null; + } + +} \ No newline at end of file diff --git a/front/src/Phaser/Entity/Sprite.ts b/front/src/Phaser/Entity/Sprite.ts new file mode 100644 index 00000000..f2abad52 --- /dev/null +++ b/front/src/Phaser/Entity/Sprite.ts @@ -0,0 +1,8 @@ +export class Sprite extends Phaser.GameObjects.Sprite { + + constructor(scene: Phaser.Scene, x: number, y: number, texture: string, frame?: number | string) { + super(scene, x, y, texture, frame); + scene.sys.updateList.add(this); + scene.sys.displayList.add(this); + } +} \ No newline at end of file diff --git a/front/src/Phaser/Game/CameraManager.ts b/front/src/Phaser/Game/CameraManager.ts index a52554e5..3b2dc06b 100644 --- a/front/src/Phaser/Game/CameraManager.ts +++ b/front/src/Phaser/Game/CameraManager.ts @@ -1,24 +1,20 @@ import {RESOLUTION} from "../../Enum/EnvironmentVariable"; import {Player} from "../Player/Player"; -import {MapManagerInterface} from "./MapManager"; +import {GameSceneInterface} from "./GameScene"; export interface CameraManagerInterface { - MapManager : MapManagerInterface; moveCamera(CurrentPlayer : Player) : void; } export class CameraManager implements CameraManagerInterface{ - Scene : Phaser.Scene; + Scene : GameSceneInterface; Camera : Phaser.Cameras.Scene2D.Camera; - MapManager : MapManagerInterface; constructor( - Scene: Phaser.Scene, + Scene: GameSceneInterface, Camera : Phaser.Cameras.Scene2D.Camera, - MapManager: MapManagerInterface, ) { this.Scene = Scene; - this.MapManager = MapManager; this.Camera = Camera; } @@ -30,8 +26,8 @@ export class CameraManager implements CameraManagerInterface{ let limit = { top: startY, left: startX, - bottom : this.MapManager.Map.heightInPixels - startY, - right: this.MapManager.Map.widthInPixels - startX, + bottom : this.Scene.Map.heightInPixels - startY, + right: this.Scene.Map.widthInPixels - startX, }; if(CurrentPlayer.x < limit.left){ diff --git a/front/src/Phaser/Game/GameManager.ts b/front/src/Phaser/Game/GameManager.ts index 71a3cbe3..d03a3152 100644 --- a/front/src/Phaser/Game/GameManager.ts +++ b/front/src/Phaser/Game/GameManager.ts @@ -50,6 +50,7 @@ export class GameManager implements GameManagerInterface { * @param UserId */ createCurrentPlayer(): void { + //Get started room send by the backend let game: GameSceneInterface = this.GameScenes.find((Game: GameSceneInterface) => Game.RoomId === ConnexionInstance.startedRoom); game.createCurrentPlayer(ConnexionInstance.userId); this.status = StatusGameManagerEnum.CURRENT_USER_CREATED; diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 61afdf68..8665fd2f 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -1,16 +1,35 @@ -import {MapManagerInterface, MapManager} from "./MapManager"; import {GameManagerInterface, StatusGameManagerEnum} from "./GameManager"; import {MessageUserPositionInterface} from "../../Connexion"; +import {CameraManager, CameraManagerInterface} from "./CameraManager"; +import {CurrentGamerInterface, GamerInterface, Player} from "../Player/Player"; +import {RESOLUTION} from "../../Enum/EnvironmentVariable"; +import Tile = Phaser.Tilemaps.Tile; + +export enum Textures { + Rock = 'rock', + Player = 'playerModel', + Map = 'map', + Tiles = 'tiles' +} export interface GameSceneInterface extends Phaser.Scene { RoomId : string; + Map: Phaser.Tilemaps.Tilemap; createCurrentPlayer(UserId : string) : void; shareUserPosition(UsersPosition : Array): void; } export class GameScene extends Phaser.Scene implements GameSceneInterface{ - private MapManager : MapManagerInterface; GameManager : GameManagerInterface; RoomId : string; + Terrain : Phaser.Tilemaps.Tileset; + Camera: CameraManagerInterface; + CurrentPlayer: CurrentGamerInterface; + MapPlayers : Phaser.Physics.Arcade.Group; + Map: Phaser.Tilemaps.Tilemap; + Layers : Array; + Objects : Array; + startX = (window.innerWidth / 2) / RESOLUTION; + startY = (window.innerHeight / 2) / RESOLUTION; constructor(RoomId : string, GameManager : GameManagerInterface) { super({ @@ -22,9 +41,10 @@ export class GameScene extends Phaser.Scene implements GameSceneInterface{ //hook preload scene preload(): void { - this.load.image('tiles', 'maps/tiles.png'); - this.load.tilemapTiledJSON('map', 'maps/map2.json'); - this.load.spritesheet('player', + this.load.image(Textures.Tiles, 'maps/tiles.png'); + this.load.tilemapTiledJSON(Textures.Map, 'maps/map2.json'); + this.load.image(Textures.Rock, 'resources/objects/rockSprite.png'); + this.load.spritesheet(Textures.Player, 'resources/characters/pipoya/Male 01-1.png', { frameWidth: 32, frameHeight: 32 } ); @@ -35,26 +55,100 @@ export class GameScene extends Phaser.Scene implements GameSceneInterface{ //hook create scene create(): void { - //create map manager - this.MapManager = new MapManager(this); + + //initalise map + this.Map = this.add.tilemap("map"); + this.Terrain = this.Map.addTilesetImage("tiles", "tiles"); + this.Map.createStaticLayer("tiles", "tiles"); + + //permit to set bound collision + this.physics.world.setBounds(0,0, this.Map.widthInPixels, this.Map.heightInPixels); + + //add layer on map + this.Layers = new Array(); + this.addLayer( this.Map.createStaticLayer("Calque 1", [this.Terrain], 0, 0).setDepth(-2) ); + this.addLayer( this.Map.createStaticLayer("Calque 2", [this.Terrain], 0, 0).setDepth(-1) ); + + //add entities + this.Objects = new Array(); + this.addSpite(this.physics.add.sprite(200, 400, Textures.Rock, 26)); + + //init event click + this.EventToClickOnTile(); + + //initialise camera + this.Camera = new CameraManager(this, this.cameras.main); + + //initialise list of other player + this.MapPlayers = this.physics.add.group({ immovable: true }); + //notify game manager can to create currentUser in map this.GameManager.createCurrentPlayer(); } - /** - * Create current player - * @param UserId - */ - createCurrentPlayer(UserId : string): void { - this.MapManager.createCurrentPlayer(UserId) + addLayer(Layer : Phaser.Tilemaps.StaticTilemapLayer){ + this.Layers.push(Layer); } - //hook update - update(dt: number): void { - if(this.GameManager.status === StatusGameManagerEnum.IN_PROGRESS){ - return; - } - this.MapManager.update(); + createCollisionWithPlayer() { + //add collision layer + this.Layers.forEach((Layer: Phaser.Tilemaps.StaticTilemapLayer) => { + this.physics.add.collider(this.CurrentPlayer, Layer, (object1: any, object2: any) => { + this.CurrentPlayer.say("Collision with layer : "+ (object2 as Tile).layer.name) + }); + Layer.setCollisionByProperty({collides: true}); + //debug code + //debug code to see the collision hitbox of the object in the top layer + Layer.renderDebug(this.add.graphics(), { + tileColor: null, //non-colliding tiles + collidingTileColor: new Phaser.Display.Color(243, 134, 48, 200), // Colliding tiles, + faceColor: new Phaser.Display.Color(40, 39, 37, 255) // Colliding face edges + }); + }); + } + + addSpite(Object : Phaser.Physics.Arcade.Sprite){ + Object.setImmovable(true); + this.Objects.push(Object); + } + + createCollisionObject(){ + this.Objects.forEach((Object : Phaser.Physics.Arcade.Sprite) => { + this.physics.add.collider(this.CurrentPlayer, Object, (object1: any, object2: any) => { + this.CurrentPlayer.say("Collision with object : " + (object2 as Phaser.Physics.Arcade.Sprite).texture.key) + }); + }) + } + + createCurrentPlayer(UserId : string){ + //initialise player + this.CurrentPlayer = new Player( + UserId, + this, + this.startX, + this.startY, + this.Camera, + ); + this.CurrentPlayer.initAnimation(); + + //create collision + this.createCollisionWithPlayer(); + this.createCollisionObject(); + } + + EventToClickOnTile(){ + // debug code to get a tile properties by clicking on it + this.input.on("pointerdown", (pointer: Phaser.Input.Pointer)=>{ + //pixel position toz tile position + let tile = this.Map.getTileAt(this.Map.worldToTileX(pointer.worldX), this.Map.worldToTileY(pointer.worldY)); + if(tile){ + this.CurrentPlayer.say("Your touch " + tile.layer.name); + } + }); + } + + update() : void { + this.CurrentPlayer.moveUser(); } /** @@ -62,6 +156,69 @@ export class GameScene extends Phaser.Scene implements GameSceneInterface{ * @param UsersPosition */ shareUserPosition(UsersPosition : Array): void { - this.MapManager.updateOrCreateMapPlayer(UsersPosition); + this.updateOrCreateMapPlayer(UsersPosition); + } + + /** + * Create new player and clean the player on the map + * @param UsersPosition + */ + updateOrCreateMapPlayer(UsersPosition : Array){ + if(!this.CurrentPlayer){ + return; + } + + //add or create new user + UsersPosition.forEach((userPosition : MessageUserPositionInterface) => { + if(userPosition.userId === this.CurrentPlayer.userId){ + return; + } + let player = this.findPlayerInMap(userPosition.userId); + if(!player){ + this.addPlayer(userPosition); + }else{ + player.updatePosition(userPosition); + } + }); + + //clean map + this.MapPlayers.getChildren().forEach((player: GamerInterface) => { + if(UsersPosition.find((message : MessageUserPositionInterface) => message.userId === player.userId)){ + return; + } + player.destroy(); + this.MapPlayers.remove(player); + }); + } + + private findPlayerInMap(UserId : string) : GamerInterface | null{ + let player = this.MapPlayers.getChildren().find((player: Player) => UserId === player.userId); + if(!player){ + return null; + } + return (player as GamerInterface); + } + + /** + * Create new player + * @param MessageUserPosition + */ + addPlayer(MessageUserPosition : MessageUserPositionInterface){ + //initialise player + let player = new Player( + MessageUserPosition.userId, + this, + MessageUserPosition.position.x, + MessageUserPosition.position.y, + this.Camera, + ); + player.initAnimation(); + this.MapPlayers.add(player); + player.updatePosition(MessageUserPosition); + + //init colision + this.physics.add.collider(this.CurrentPlayer, player, (CurrentPlayer: CurrentGamerInterface, MapPlayer: GamerInterface) => { + CurrentPlayer.say("Hello, how are you ? "); + }); } } diff --git a/front/src/Phaser/Game/MapManager.ts b/front/src/Phaser/Game/MapManager.ts deleted file mode 100644 index a66e0d37..00000000 --- a/front/src/Phaser/Game/MapManager.ts +++ /dev/null @@ -1,148 +0,0 @@ -import {CameraManager, CameraManagerInterface} from "./CameraManager"; -import {RESOLUTION} from "../../Enum/EnvironmentVariable"; -import {CurrentGamerInterface, GamerInterface, Player} from "../Player/Player"; -import {GameSceneInterface} from "./GameScene"; -import {MessageUserPositionInterface} from "../../Connexion"; - -export interface MapManagerInterface { - keyZ: Phaser.Input.Keyboard.Key; - keyQ: Phaser.Input.Keyboard.Key; - keyS: Phaser.Input.Keyboard.Key; - keyD: Phaser.Input.Keyboard.Key; - keyRight: Phaser.Input.Keyboard.Key; - keyLeft: Phaser.Input.Keyboard.Key; - keyUp: Phaser.Input.Keyboard.Key; - keyDown: Phaser.Input.Keyboard.Key; - keyShift: Phaser.Input.Keyboard.Key; - - Map: Phaser.Tilemaps.Tilemap; - Terrain: Phaser.Tilemaps.Tileset; - Camera: CameraManagerInterface; - Scene: GameSceneInterface; - - createCurrentPlayer(UserId : string): void; - update(): void; - updateOrCreateMapPlayer(UsersPosition : Array): void; -} -export class MapManager implements MapManagerInterface{ - keyZ: Phaser.Input.Keyboard.Key; - keyQ: Phaser.Input.Keyboard.Key; - keyS: Phaser.Input.Keyboard.Key; - keyD: Phaser.Input.Keyboard.Key; - keyRight: Phaser.Input.Keyboard.Key; - keyLeft: Phaser.Input.Keyboard.Key; - keyUp: Phaser.Input.Keyboard.Key; - keyDown: Phaser.Input.Keyboard.Key; - keyShift: Phaser.Input.Keyboard.Key; - - Terrain : Phaser.Tilemaps.Tileset; - Camera: CameraManagerInterface; - CurrentPlayer: CurrentGamerInterface; - MapPlayers : GamerInterface[]; - Scene: GameSceneInterface; - Map: Phaser.Tilemaps.Tilemap; - startX = (window.innerWidth / 2) / RESOLUTION; - startY = (window.innerHeight / 2) / RESOLUTION; - - constructor(scene: GameSceneInterface){ - this.Scene = scene; - - //initalise map - this.Map = this.Scene.add.tilemap("map"); - this.Terrain = this.Map.addTilesetImage("tiles", "tiles"); - this.Map.createStaticLayer("tiles", "tiles"); - this.Map.createStaticLayer("Calque 1", [this.Terrain], 0, 0); - this.Map.createStaticLayer("Calque 2", [this.Terrain], 0, 0); - - //initialise keyboard - this.initKeyBoard(); - //initialise camera - this.Camera = new CameraManager(this.Scene, this.Scene.cameras.main, this); - //initialise list of other player - this.MapPlayers = new Array(); - } - - createCurrentPlayer(UserId : string){ - //initialise player - this.CurrentPlayer = new Player( - UserId, - this.Scene, - this.startX, - this.startY, - this.Camera, - this - ); - this.CurrentPlayer.initAnimation(); - } - - initKeyBoard() { - this.keyShift = this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SHIFT); - - this.keyZ = this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.Z); - this.keyQ = this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.Q); - this.keyS = this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.S); - this.keyD = this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.D); - - this.keyUp = this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.UP); - this.keyLeft = this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.LEFT); - this.keyDown = this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.DOWN); - this.keyRight = this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.RIGHT); - } - - update() : void { - this.CurrentPlayer.move(); - } - - /** - * Create new player and clean the player on the map - * @param UsersPosition - */ - updateOrCreateMapPlayer(UsersPosition : Array){ - if(!this.CurrentPlayer){ - return; - } - - //add or create new user - UsersPosition.forEach((userPosition : MessageUserPositionInterface) => { - if(userPosition.userId === this.CurrentPlayer.userId){ - return; - } - let player = this.MapPlayers.find((player: Player) => userPosition.userId === player.userId); - if(!player){ - this.addPlayer(userPosition); - }else{ - player.updatePosition(userPosition); - } - }); - - //clean map - let mapPlayers = new Array(); - this.MapPlayers.forEach((player: Player) => { - if(UsersPosition.find((message : MessageUserPositionInterface) => message.userId === player.userId)){ - mapPlayers.push(player); - return; - } - player.destroy(); - }); - this.MapPlayers = mapPlayers; - } - - /** - * Create new player - * @param MessageUserPosition - */ - addPlayer(MessageUserPosition : MessageUserPositionInterface){ - //initialise player - let player = new Player( - MessageUserPosition.userId, - this.Scene, - MessageUserPosition.position.x, - MessageUserPosition.position.y, - this.Camera, - this - ); - player.initAnimation(); - this.MapPlayers.push(player); - player.updatePosition(MessageUserPosition) - } -} \ No newline at end of file diff --git a/front/src/Phaser/NonPlayer/NonPlayer.ts b/front/src/Phaser/NonPlayer/NonPlayer.ts new file mode 100644 index 00000000..63012e46 --- /dev/null +++ b/front/src/Phaser/NonPlayer/NonPlayer.ts @@ -0,0 +1,40 @@ +import {PlayableCaracter} from "../Entity/PlayableCaracter"; +import {Textures} from "../Game/GameScene"; +import {UserInputEvent} from "../UserInput/UserInputManager"; +import {Player} from "../Player/Player"; +import {MessageUserPositionInterface} from "../../Connexion"; +import {playAnimation} from "../Player/Animation"; + +export class NonPlayer extends PlayableCaracter { + + isFleeing: boolean = false; + fleeingDirection:any = null //todo create a vector class + + constructor(scene: Phaser.Scene, x: number, y: number) { + super(scene, x, y, Textures.Player, 1); + this.setSize(32, 32); //edit the hitbox to better match the caracter model + } + + + updatePosition(MessageUserPosition : MessageUserPositionInterface){ + playAnimation(this, MessageUserPosition.position.direction); + this.setX(MessageUserPosition.position.x); + this.setY(MessageUserPosition.position.y); + } + + fleeFrom(player:Player) { + if (this.isFleeing) return; + this.say("Don't touch me!"); + this.isFleeing = true; + + setTimeout(() => { + this.say("Feww, I escaped."); + this.isFleeing = false + this.fleeingDirection = null + }, 3000); + + let vectorX = this.x - player.x; + let vectorY = this.y - player.y; + this.fleeingDirection = {x: vectorX, y: vectorY} + } +} \ No newline at end of file diff --git a/front/src/Phaser/Player/Animation.ts b/front/src/Phaser/Player/Animation.ts index eb8298f4..48f34682 100644 --- a/front/src/Phaser/Player/Animation.ts +++ b/front/src/Phaser/Player/Animation.ts @@ -1,3 +1,5 @@ +import {Textures} from "../Game/GameScene"; + interface AnimationData { key: string; frameRate: number; @@ -15,31 +17,31 @@ export enum PlayerAnimationNames { None = 'none', } -export const getPlayerAnimations = (PlayerValue : string): AnimationData[] => { +export const getPlayerAnimations = (): AnimationData[] => { return [{ key: PlayerAnimationNames.WalkDown, - frameModel: PlayerValue, + frameModel: Textures.Player, frameStart: 0, frameEnd: 2, frameRate: 10, repeat: -1 }, { key: PlayerAnimationNames.WalkLeft, - frameModel: PlayerValue, + frameModel: Textures.Player, frameStart: 3, frameEnd: 5, frameRate: 10, repeat: -1 }, { key: PlayerAnimationNames.WalkRight, - frameModel: PlayerValue, + frameModel: Textures.Player, frameStart: 6, frameEnd: 8, frameRate: 10, repeat: -1 }, { key: PlayerAnimationNames.WalkUp, - frameModel: PlayerValue, + frameModel: Textures.Player, frameStart: 9, frameEnd: 11, frameRate: 10, @@ -53,4 +55,4 @@ export const playAnimation = (Player : Phaser.GameObjects.Sprite, direction : st } else if (direction === PlayerAnimationNames.None && Player.anims.currentAnim) { Player.anims.currentAnim.destroy(); } -}; +} diff --git a/front/src/Phaser/Player/Player.ts b/front/src/Phaser/Player/Player.ts index f256bbf4..d0169297 100644 --- a/front/src/Phaser/Player/Player.ts +++ b/front/src/Phaser/Player/Player.ts @@ -1,135 +1,113 @@ -import {MapManagerInterface} from "../Game/MapManager"; import {getPlayerAnimations, playAnimation, PlayerAnimationNames} from "./Animation"; -import {GameSceneInterface} from "../Game/GameScene"; +import {GameSceneInterface, Textures} from "../Game/GameScene"; import {ConnexionInstance} from "../Game/GameManager"; import {CameraManagerInterface} from "../Game/CameraManager"; import {MessageUserPositionInterface} from "../../Connexion"; +import {ActiveEventList, UserInputEvent, UserInputManager} from "../UserInput/UserInputManager"; +import {PlayableCaracter} from "../Entity/PlayableCaracter"; -export interface CurrentGamerInterface{ +export interface CurrentGamerInterface extends PlayableCaracter{ userId : string; - MapManager : MapManagerInterface; PlayerValue : string; CameraManager: CameraManagerInterface; initAnimation() : void; - move() : void; + moveUser() : void; + say(text : string) : void; } -export interface GamerInterface{ +export interface GamerInterface extends PlayableCaracter{ userId : string; - MapManager : MapManagerInterface; PlayerValue : string; CameraManager: CameraManagerInterface; initAnimation() : void; updatePosition(MessageUserPosition : MessageUserPositionInterface) : void; + say(text : string) : void; } -export class Player extends Phaser.GameObjects.Sprite implements CurrentGamerInterface, GamerInterface{ - userId : string; - MapManager : MapManagerInterface; - PlayerValue : string; +export class Player extends PlayableCaracter implements CurrentGamerInterface, GamerInterface { + userId: string; + PlayerValue: string; CameraManager: CameraManagerInterface; + userInputManager: UserInputManager; constructor( userId: string, - Scene : GameSceneInterface, - x : number, - y : number, + Scene: GameSceneInterface, + x: number, + y: number, CameraManager: CameraManagerInterface, - MapManager: MapManagerInterface, - PlayerValue : string = "player" + PlayerValue: string = Textures.Player ) { - super(Scene, x, y, PlayerValue); + super(Scene, x, y, PlayerValue, 1); + + //create input to move + this.userInputManager = new UserInputManager(Scene); + + //set data this.userId = userId; this.PlayerValue = PlayerValue; - Scene.add.existing(this); - this.MapManager = MapManager; this.CameraManager = CameraManager; + + //the current player model should be push away by other players to prevent conflict + this.setImmovable(false); + //edit the hitbox to better match the caracter model + this.setSize(32, 32); } - initAnimation() : void{ - getPlayerAnimations(this.PlayerValue).forEach(d => { + initAnimation(): void { + getPlayerAnimations().forEach(d => { this.scene.anims.create({ key: d.key, - frames: this.scene.anims.generateFrameNumbers(d.frameModel, { start: d.frameStart, end: d.frameEnd }), + frames: this.scene.anims.generateFrameNumbers(d.frameModel, {start: d.frameStart, end: d.frameEnd}), frameRate: d.frameRate, repeat: d.repeat }); }) } - move() : void{ + moveUser(): void { //if user client on shift, camera and player speed - let speedMultiplier = this.MapManager.keyShift.isDown ? 5 : 1; let haveMove = false; let direction = null; - if((this.MapManager.keyZ.isDown || this.MapManager.keyUp.isDown)){ - if(!this.CanMoveUp()){ - return; - } - playAnimation(this, PlayerAnimationNames.WalkUp); - this.setY(this.y - (2 * speedMultiplier)); + let activeEvents = this.userInputManager.getEventListForGameTick(); + let speedMultiplier = activeEvents.get(UserInputEvent.SpeedUp) ? 500 : 100; + + if (activeEvents.get(UserInputEvent.MoveUp)) { + this.move(0, -speedMultiplier); haveMove = true; direction = PlayerAnimationNames.WalkUp; } - if((this.MapManager.keyQ.isDown || this.MapManager.keyLeft.isDown)){ - if(!this.CanMoveLeft()){ - return; - } - playAnimation(this, PlayerAnimationNames.WalkLeft); - this.setX(this.x - (2 * speedMultiplier)); + if (activeEvents.get(UserInputEvent.MoveLeft)) { + this.move(-speedMultiplier, 0); haveMove = true; direction = PlayerAnimationNames.WalkLeft; } - if((this.MapManager.keyS.isDown || this.MapManager.keyDown.isDown)){ - if(!this.CanMoveDown()){ - return; - } - playAnimation(this, PlayerAnimationNames.WalkDown); - this.setY(this.y + (2 * speedMultiplier)); + if (activeEvents.get(UserInputEvent.MoveDown)) { + this.move(0, speedMultiplier); haveMove = true; direction = PlayerAnimationNames.WalkDown; } - if((this.MapManager.keyD.isDown || this.MapManager.keyRight.isDown)){ - if(!this.CanMoveRight()){ - return; - } - playAnimation(this, PlayerAnimationNames.WalkRight); - this.setX(this.x + (2 * speedMultiplier)); + if (activeEvents.get(UserInputEvent.MoveRight)) { + this.move(speedMultiplier, 0); haveMove = true; direction = PlayerAnimationNames.WalkRight; } - if(!haveMove){ - playAnimation(this, PlayerAnimationNames.None); + if (!haveMove) { direction = PlayerAnimationNames.None; + this.stop(); } this.sharePosition(direction); this.CameraManager.moveCamera(this); } - private sharePosition(direction : string){ - if(ConnexionInstance) { + private sharePosition(direction: string) { + if (ConnexionInstance) { ConnexionInstance.sharePosition((this.scene as GameSceneInterface).RoomId, this.x, this.y, direction); } } - private CanMoveUp(){ - return this.y > 0; - } - - private CanMoveLeft(){ - return this.x > 0; - } - - private CanMoveDown(){ - return this.MapManager.Map.heightInPixels > this.y; - } - - private CanMoveRight(){ - return this.MapManager.Map.widthInPixels > this.x; - } - - updatePosition(MessageUserPosition : MessageUserPositionInterface){ + updatePosition(MessageUserPosition: MessageUserPositionInterface) { playAnimation(this, MessageUserPosition.position.direction); this.setX(MessageUserPosition.position.x); this.setY(MessageUserPosition.position.y); diff --git a/front/src/Phaser/UserInput/UserInputManager.ts b/front/src/Phaser/UserInput/UserInputManager.ts new file mode 100644 index 00000000..6c5b32c2 --- /dev/null +++ b/front/src/Phaser/UserInput/UserInputManager.ts @@ -0,0 +1,68 @@ +import Map = Phaser.Structs.Map; +import {GameSceneInterface} from "../Game/GameScene"; + +interface UserInputManagerDatum { + keyCode: number; + keyInstance: Phaser.Input.Keyboard.Key; + event: UserInputEvent +} + +export enum UserInputEvent { + MoveLeft = 1, + MoveUp, + MoveRight, + MoveDown, + SpeedUp, + Interact, + Shout, +} + +//we cannot the map structure so we have to create a replacment +export class ActiveEventList { + private KeysCode : any; + constructor() { + this.KeysCode = {}; + } + get(event: UserInputEvent): boolean { + return this.KeysCode[event] || false; + } + set(event: UserInputEvent, value: boolean): boolean { + return this.KeysCode[event] = true; + } +} + +//this class is responsible for catching user inputs and listing all active user actions at every game tick events. +export class UserInputManager { + private KeysCode: UserInputManagerDatum[] = [ + {keyCode: Phaser.Input.Keyboard.KeyCodes.Z, event: UserInputEvent.MoveUp, keyInstance: null}, + {keyCode: Phaser.Input.Keyboard.KeyCodes.Q, event: UserInputEvent.MoveLeft, keyInstance: null}, + {keyCode: Phaser.Input.Keyboard.KeyCodes.S, event: UserInputEvent.MoveDown, keyInstance: null}, + {keyCode: Phaser.Input.Keyboard.KeyCodes.D, event: UserInputEvent.MoveRight, keyInstance: null}, + + {keyCode: Phaser.Input.Keyboard.KeyCodes.UP, event: UserInputEvent.MoveUp, keyInstance: null}, + {keyCode: Phaser.Input.Keyboard.KeyCodes.LEFT, event: UserInputEvent.MoveLeft, keyInstance: null}, + {keyCode: Phaser.Input.Keyboard.KeyCodes.DOWN, event: UserInputEvent.MoveDown, keyInstance: null}, + {keyCode: Phaser.Input.Keyboard.KeyCodes.RIGHT, event: UserInputEvent.MoveRight, keyInstance: null}, + + {keyCode: Phaser.Input.Keyboard.KeyCodes.SHIFT, event: UserInputEvent.SpeedUp, keyInstance: null}, + + {keyCode: Phaser.Input.Keyboard.KeyCodes.E, event: UserInputEvent.Interact, keyInstance: null}, + {keyCode: Phaser.Input.Keyboard.KeyCodes.F, event: UserInputEvent.Shout, keyInstance: null}, + ]; + + constructor(Scene : GameSceneInterface) { + this.KeysCode.forEach(d => { + d.keyInstance = Scene.input.keyboard.addKey(d.keyCode); + }); + } + + getEventListForGameTick(): ActiveEventList { + let eventsMap = new ActiveEventList(); + this.KeysCode.forEach(d => { + if (d. keyInstance.isDown) { + eventsMap.set(d.event, true); + } + }); + return eventsMap; + } +} \ No newline at end of file diff --git a/front/src/index.ts b/front/src/index.ts index 2a18110d..5d90a19e 100644 --- a/front/src/index.ts +++ b/front/src/index.ts @@ -12,6 +12,12 @@ const config: GameConfig = { parent: "game", scene: gameManager.GameScenes, zoom: RESOLUTION, + physics: { + default: "arcade", + arcade: { + debug: true + } + } }; gameManager.createGame().then(() => {