added entitys, enemys, bats and custom attack hitboxes for players

added map loading and one-way collision (needs to move to tiled attribute tho)

Signed-off-by: Pascal Syma <pascal@syma.dev>
This commit is contained in:
2020-05-20 11:13:21 +02:00
parent 4a22dbe657
commit a726db1306
22 changed files with 2166 additions and 30 deletions
+229
View File
@@ -0,0 +1,229 @@
/*
* Copyright (c) 2020. Pascal Syma <pascal@syma.dev>
* All rights reserved.
*/
import Player from "../entities/Player";
import Bat from "../entities/Enemys/Bat";
export default class Game extends Phaser.Scene {
constructor() {
super({
key: "Game",
pixelArt: true,
physics: {
default: 'arcade',
arcade: {
gravity: {y: 300},
// debug: true
}
}
});
}
static setTileCollision(mapLayer, idxOrArray, dirs) {
let mFunc; // tile index matching function
if (idxOrArray.length) {
// if idxOrArray is an array, use a function with a loop
mFunc = function (inp) {
for (let i = 0; i < idxOrArray.length; i++) {
if (idxOrArray[i] === inp - 1) {
return true;
}
}
return false;
};
} else {
// if idxOrArray is a single number, use a simple function
mFunc = function (inp) {
return inp - 1 === idxOrArray;
};
}
// get the 2-dimensional tiles array for this layer
// const d = mapLayer.map.layers[mapLayer.index].data;
const d = mapLayer.layer.data;
for (let i = 0; i < d.length; i++) {
for (let j = 0; j < d[i].length; j++) {
const t = d[i][j];
if (mFunc(t.index)) {
t.collideUp = dirs.top;
t.collideDown = dirs.bottom;
t.collideLeft = dirs.left;
t.collideRight = dirs.right;
t.faceTop = dirs.top;
t.faceBottom = dirs.bottom;
t.faceLeft = dirs.left;
t.faceRight = dirs.right;
}
}
}
}
create() {
this.anims.create({
key: 'idle',
frames: this.anims.generateFrameNumbers('char', {start: 126, end: 131}),
frameRate: 10,
repeat: -1
});
this.anims.create({
key: 'run',
frames: this.anims.generateFrameNumbers('char', {start: 154, end: 161}),
frameRate: 10,
repeat: -1
});
this.anims.create({
key: 'jump_start',
frames: this.anims.generateFrameNumbers('char', {start: 140, end: 143}),
frameRate: 10
});
this.anims.create({
key: 'jump',
frames: this.anims.generateFrameNumbers('char', {start: 144, end: 145}),
frameRate: 10,
repeat: -1
});
this.anims.create({
key: 'attack_a',
frames: this.anims.generateFrameNumbers('char', {start: 0, end: 13}),
frameRate: 15
});
this.anims.create({
key: 'attack_b',
frames: this.anims.generateFrameNumbers('char', {start: 14, end: 26}),
frameRate: 15
});
this.anims.create({
key: 'attack_c',
frames: this.anims.generateFrameNumbers('char', {start: 28, end: 41}),
frameRate: 15
});
this.anims.create({
key: 'die',
frames: this.anims.generateFrameNumbers('char', {start: 42, end: 51}),
frameRate: 10
});
this.anims.create({
key: 'batIdle',
frames: this.anims.generateFrameNumbers('batFly', {start: 0, end: 5}),
frameRate: 10,
repeat: -1
});
this.anims.create({
key: 'batDie',
frames: this.anims.generateFrameNumbers('batDie', {start: 0, end: 4}),
frameRate: 15
});
const map = this.make.tilemap({key: "map"});
const tileset = map.addTilesetImage("tileset", "tiles", 16, 16, 1, 2);
this.backLayer = map.createStaticLayer("Back", tileset, 0, 0);
this.backLayer2 = map.createStaticLayer("Back2", tileset, 0, 0);
this.worldLayer = map.createDynamicLayer("Main", tileset, 0, 0);
const spawnPoint = map.findObject("Objects", obj => obj.name === "player");
this.enemys = this.add.group();
this.player = new Player(this, spawnPoint, 'char');
const batSpawns = map.objects[0].objects.filter(obj => obj.name === "bat");
batSpawns.forEach(s => {
let bat = new Bat(this, s);
bat.fly(s.type === "" ? 90 : parseInt(s.type));
this.enemys.add(bat)
});
this.worldLayer.setCollisionByProperty({collides: true});
Game.setTileCollision(this.worldLayer, [29, 30, 31, 39], {
top: true,
bottom: false,
right: false,
left: false
})
this.physics.world.bounds.width = this.backLayer.width;
this.physics.world.bounds.height = this.backLayer.height;
this.cameras.main.setBounds(0, 0, map.widthInPixels, map.heightInPixels);
this.cameras.main.setBackgroundColor('#ccccff');
this.cameras.main.startFollow(this.player);
this.physics.add.collider(this.player, this.worldLayer);
this.physics.add.overlap(this.player.attackHitbox, this.enemys, (h, b) => {
if (h.getData('active') && h.overlap !== undefined) {
h.overlap(b);
}
})
this.keyW = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.W);
this.keyS = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.S);
this.keyA = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.A);
this.keyD = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.D);
this.keySpace = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE);
this.keyUp = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.UP);
this.keyLeft = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.LEFT);
this.keyRight = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.RIGHT);
this.keyDown = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.DOWN);
this.keyDie = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.C);
}
update(time, delta) {
this.enemys.getChildren().forEach(c => {
c.update()
});
this.player.update(time, delta);
}
dieActive() {
return this.keyDie.isDown;
}
rightActive() {
return this.keyRight.isDown || this.keyD.isDown/* || (this.game.input.activePointer.isDown &&
this.game.input.activePointer.x > 2*this.game.config.width/3)*/;
}
leftActive() {
return this.keyLeft.isDown || this.keyA.isDown/* || (this.game.input.activePointer.isDown &&
this.game.input.activePointer.x < this.game.config.width/3)*/;
}
downActive() {
return this.keyDown.isDown || this.keyS.isDown/* || (this.game.input.activePointer.isDown &&
this.game.input.activePointer.y > 2*this.game.config.height/3)*/;
}
upActive() {
return this.keyUp.isDown || this.keyW.isDown/* || (this.game.input.activePointer.isDown &&
this.game.input.activePointer.y < this.game.config.height/3)*/;
}
}
+42 -18
View File
@@ -3,29 +3,53 @@
* All rights reserved.
*/
import logoImg from "../assets/logo.png";
import Player from "../entities/Player";
export default class Menu extends Phaser.Scene {
constructor() {
super("Menu");
}
preload() {
this.load.image("logo", logoImg);
}
create() {
const logo = this.add.image(this.cameras.main.centerX, this.cameras.main.centerY * 1.5, "logo").setOrigin(0.5, 0.5);
this.tweens.add({
targets: logo,
y: this.cameras.main.centerY,
duration: 2000,
ease: "Power2",
yoyo: true,
loop: -1
super({
key: "Menu",
pixelArt: true,
physics: {
default: 'arcade',
arcade: {
gravity: {y: 0},
// debug: true
}
}
});
}
create() {
const w = this.cameras.main.centerX;
const h = this.cameras.main.centerY;
[{x: w / 2, y: h / 2, key: 'A'},
{x: w / 2 + w, y: h / 2, key: 'B'},
{x: w / 2, y: h / 2 + h, key: 'C'},
{x: w / 2 + w, y: h / 2 + h, key: 'D'}]
.forEach((e) => this.addOption(e));
}
addOption(options) {
this.anims.create({
key: 'idle' + options.key,
frames: this.anims.generateFrameNumbers('char' + options.key, {start: 126, end: 131}),
frameRate: 10,
repeat: -1
});
new Player(this, options, 'char' + options.key).play('idle' + options.key, true)
.setInteractive()
.on('pointerdown', () => {
this.textures.addSpriteSheet('char', this.textures.get('char' + options.key).getSourceImage(), {
frameWidth: 79,
frameHeight: 63
});
this.scene.start('Game');
})
}
}
+89
View File
@@ -0,0 +1,89 @@
/*
* Copyright (c) 2020. Pascal Syma <pascal@syma.dev>
* All rights reserved.
*/
import logoImg from "../../public/favicon.png";
import map from "../assets/map.json";
import charA from "../assets/char_a/spritesheet.png";
import charB from "../assets/char_b/spritesheet.png";
import charC from "../assets/char_c/spritesheet.png";
import charD from "../assets/char_d/spritesheet.png";
import tiles from "../assets/tileset.png";
import batFly from "../assets/BatFly.png";
import batDie from "../assets/BatDie.png";
export default class Preload extends Phaser.Scene {
constructor() {
super('Loading');
}
preload() {
this.loadingScreen();
this.load.image("logo", logoImg);
this.load.spritesheet('charA', charA, {frameWidth: 79, frameHeight: 63/*, margin: 1, spacing: 2*/});
this.load.spritesheet('charB', charB, {frameWidth: 79, frameHeight: 63/*, margin: 1, spacing: 2*/});
this.load.spritesheet('charC', charC, {frameWidth: 79, frameHeight: 63/*, margin: 1, spacing: 2*/});
this.load.spritesheet('charD', charD, {frameWidth: 79, frameHeight: 63/*, margin: 1, spacing: 2*/});
this.load.spritesheet('tiles', tiles, {frameWidth: 16, frameHeight: 16, margin: 1, spacing: 2});
this.load.spritesheet('batFly', batFly, {frameWidth: 48, frameHeight: 32/*, margin: 1, spacing: 2*/});
this.load.spritesheet('batDie', batDie, {frameWidth: 48, frameHeight: 32/*, margin: 1, spacing: 2*/});
this.load.tilemapTiledJSON("map", map);
}
create() {
this.add.image(this.cameras.main.centerX, this.cameras.main.centerY, 'logo').setOrigin(0.5, 0.5).setDisplaySize(this.cameras.main.centerX, this.cameras.main.centerX);
setTimeout(() => {
this.scene.start('Menu');
}, 2 * 1000)
}
loadingScreen() {
const progressBar = this.add.graphics();
const progressBox = this.add.graphics();
const width = this.cameras.main.width;
const height = this.cameras.main.height;
progressBox.fillStyle(0x222222, 0.8);
progressBox.fillRect(width / 4, height / 2 - 25, width / 2, 50);
const loadingText = this.make.text({
x: width / 2,
y: height / 2 - 25 - 20,
text: 'Loading...',
style: {
font: '20px monospace',
fill: '#ffffff'
}
});
loadingText.setOrigin(0.5, 1);
const percentText = this.make.text({
x: width / 2,
y: height / 2,
text: '0%',
style: {
font: '18px monospace',
fill: '#ffffff'
}
});
percentText.setOrigin(0.5, 0.5);
this.load.on('progress', (value) => {
percentText.setText(parseInt(value * 100) + '%');
progressBar.clear();
progressBar.fillStyle(0xffffff, 1);
progressBar.fillRect(width / 4 + 10, height / 2 - 25 + 10, (width / 2 - 20) * value, 50 - 20);
});
this.load.on('complete', () => {
progressBar.destroy();
progressBox.destroy();
loadingText.destroy();
percentText.destroy();
});
}
}