Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Animation angle and rotation has impact on a hitbox and are synced with client #274

Open
wants to merge 4 commits into
base: v4.2.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/client/src/Effects/Animation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -307,4 +307,4 @@ export class Animation extends Sprite {
this.frameIndex++
}
}
}
}
4 changes: 2 additions & 2 deletions packages/client/src/Sprite/Character.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,8 @@ export default class Character extends Sprite {
this.spritesheet[hook](this)
}
else if (this.animation.has(name)) {
this.animation.play(name, [this.data.direction])
this.animation.play(name, [this.data.direction, this.data.angle, this.data.rotation])
}
}

}
}
28 changes: 22 additions & 6 deletions packages/common/src/AbstractObject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ export class AbstractObject {
behavior: Behavior = Behavior.Direction

hitbox: SAT.Box
angle: number = 0
rotation: number = 0

inShapes: {
[shapeId: string]: RpgShape
Expand Down Expand Up @@ -415,7 +417,7 @@ export class AbstractObject {
if (event.id == this.id) continue
if (!this.zCollision(event)) continue

const collided = Hit.testPolyCollision(HitType.Box, hitbox, event.hitbox)
const collided = Hit.testPolyCollision(Hit.getType(hitbox), hitbox, event.hitbox)

for (let shape of this.shapes) {
await this.collisionWithShape(shape, event)
Expand Down Expand Up @@ -473,12 +475,15 @@ export class AbstractObject {
else {
position = player.position.copy()
}
let { angle, rotation } = player;
const hitboxObj = Hit.createObjectHitbox(
position.x,
position.y,
position.z,
hitbox.w,
hitbox.h
hitbox.h,
angle,
rotation
)
let collided = Hit.testPolyCollision(shape.type, hitboxObj, shape.hitbox)
const playerPositionSaved = player.position.copy()
Expand Down Expand Up @@ -540,7 +545,16 @@ export class AbstractObject {
const pull = (target.copy().subtract(nextPosition)).multiply((1 / pullDistance))
const totalPush = new Vector2dZero()
let contenders = 0
const hitbox = Hit.createObjectHitbox(nextPosition.x, nextPosition.y, nextPosition.z, this.hitbox.w, this.hitbox.h)
const { angle, rotation } = this;
const hitbox = Hit.createObjectHitbox(
nextPosition.x,
nextPosition.y,
nextPosition.z,
this.hitbox.w,
this.hitbox.h,
angle,
rotation
)

const createObstacle = function (x: number, y: number, radius: number): Vector2d {
const obstacle = new Vector2d(x, y)
Expand All @@ -564,7 +578,8 @@ export class AbstractObject {
}, (index) => {
if (index < 0) return
const pos = this.mapInstance.getTilePosition(index)
const hitbox = Hit.createObjectHitbox(pos.x, pos.y, nextPosition.z, this.hitbox.w, this.hitbox.h)
const { angle, rotation } = this
const hitbox = Hit.createObjectHitbox(pos.x, pos.y, nextPosition.z, this.hitbox.w, this.hitbox.h, angle, rotation)
const radius = this.mapInstance.tilewidth / 2
const tile = this.getTile(pos.x, pos.y, nextPosition.z, hitbox)
if (tile.hasCollision) {
Expand Down Expand Up @@ -603,7 +618,8 @@ export class AbstractObject {
this.collisionWith = []
this._collisionWithTiles = []
const prevMapId = this.map
const hitbox = Hit.createObjectHitbox(nextPosition.x, nextPosition.y, 0, this.hitbox.w, this.hitbox.h)
const { angle, rotation } = this;
const hitbox = Hit.createObjectHitbox(nextPosition.x, nextPosition.y, 0, this.hitbox.w, this.hitbox.h, angle, rotation)
const boundingMap = this.mapInstance?.boundingMap(nextPosition, this.hitbox)
let collided = false

Expand Down Expand Up @@ -971,4 +987,4 @@ export interface AbstractObject {
execMethod(methodName: string, methodData?, instance?)
changeMap(mapName: string)
syncChanges()
}
}
25 changes: 21 additions & 4 deletions packages/common/src/Hit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,19 @@ export { HitType, HitObject } from '@rpgjs/types'

class HitClass {

createObjectHitbox(x: number, y: number, z: number, w: number, h: number): SAT.Box {
return new SAT.Box(new SAT.Vector(x, y - z), w, h)
createObjectHitbox(x: number, y: number, z: number, w: number, h: number, angle: number = 0, rotation: number = 0): SAT.Box {
const box = new SAT.Box(new SAT.Vector(x, y - z), w, h)

if (angle) {
return box.toPolygon().setAngle(angle)
}

if (rotation) {
return box.toPolygon().rotate(rotation)
}

return box;
}

getHitbox(obj: HitObject, offset?: { x: number, y: number }): {
hitbox: SAT,
type?: string,
Expand Down Expand Up @@ -65,6 +74,14 @@ class HitClass {
}
return collided
}

getType(hit: SAT): string {
if (isInstanceOf(hit, SAT.Box)) return HitType.Box
if (isInstanceOf(hit, SAT.Circle)) return HitType.Circle
if (isInstanceOf(hit, SAT.Polygon)) return HitType.Polygon

throw new Error('Unsupported type of hitbox');
}
}

export const Hit = new HitClass()
export const Hit = new HitClass()
2 changes: 2 additions & 0 deletions packages/sample4/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
dist
node_modules
15 changes: 15 additions & 0 deletions packages/sample4/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
FROM node:18 as build
WORKDIR /build
ADD . /build
RUN npm i
ENV NODE_ENV=production
RUN npm run build

FROM node:18-alpine
WORKDIR /game
COPY --from=build /build/dist ./dist
COPY --from=build /build/package*.json ./
ENV NODE_ENV=production
RUN npm i
EXPOSE 3000
CMD npm start
52 changes: 52 additions & 0 deletions packages/sample4/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<!doctype html>
<html>

<head>
<meta charset="utf-8">
<title>RPG Game</title>
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, minimum-scale=1, width=device-width, height=device-height">
</head>

<body>
<div id="rpg"></div>

<style>
body,
html {
height: 100%;
overflow: hidden;
}

body {
margin: 0;
background-color: black;
display: flex;
align-items: center;
justify-content: center;
}

#rpg {
position: relative;

}

@media (min-width: 800px) {
#rpg {
width: 816px;
height: 624px;
}
}

@media (max-width: 800px) {
#rpg {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
}
</style>
</body>

</html>
18 changes: 18 additions & 0 deletions packages/sample4/main/events/ArrowBullet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { EventData, RpgEvent } from '@rpgjs/server';

// @ts-ignore
@EventData({
name: 'arrow-bullet',
hitbox: {
width: 31,
height: 2
}
})
export default class ArrowBullet extends RpgEvent {
onInit() {
this.speed = 5;
this.setGraphic('arrow');
this.through = true;
this.throughOtherPlayer = true;
}
}
32 changes: 32 additions & 0 deletions packages/sample4/main/events/villager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { RpgEvent, EventData, RpgPlayer, Move, Components } from '@rpgjs/server'

@EventData({
name: 'EV-1',
hitbox: {
width: 32,
height: 16
}
})
export default class VillagerEvent extends RpgEvent {
onInit() {
this.setGraphic('female')

this.speed = 1.2;
// this.infiniteMoveRoute([
// Move.tileRandom()
// ])
this.setComponentsTop(Components.hpBar())
}
async onAction(player: RpgPlayer) {
this.getCurrentMap()?.createShape({
x: this.position.x,
y: this.position.y,
name: 'rectangle',
width: 32,
height: 16,
properties: {
color: '#ff0000'
}
});
}
}
49 changes: 49 additions & 0 deletions packages/sample4/main/player.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { RpgPlayer, type RpgPlayerHooks, Control, Components } from '@rpgjs/server'
import { PositionXY } from '@rpgjs/types'
import { PhysicalAttack } from './src/PhysicalAttack';
import { Vector2d } from "@rpgjs/common/src/Vector2d";
import { ExtendedMoveManager } from './src/managers/ExtendedMovedManager';
import Utils from '@rpgjs/common/src/Utils';

declare module '@rpgjs/server' {
export interface Player extends ExtendedMoveManager {}
}

const player: RpgPlayerHooks = {
onConnected(player: RpgPlayer) {
player.name = 'Guest'
player.setComponentsTop(Components.text('{name}'))
},
onInput(player: RpgPlayer, { input }) {
if (input == Control.Back) {
player.callMainMenu()
}
},
async onJoinMap(player: RpgPlayer) {
player.off('shoot-bullet');
player.on('shoot-bullet', (position: PositionXY) => {
const map = player.getCurrentMap();

if (!map) {
return;
}

const startVector = new Vector2d(Math.round(player.position.x), Math.round(player.position.y));
const endVector = new Vector2d(Math.round(position.x), Math.round(position.y));
const distance = 300;
const targetVector = endVector.copy().subtract(startVector.copy());
targetVector.normalize();
targetVector.multiply(distance);
targetVector.add(startVector);

player.stopMoveTo();
player.breakRoutes();

PhysicalAttack.shoot(player, targetVector);
});
}
}

Utils.applyMixins(RpgPlayer, [ExtendedMoveManager]);

export default player
14 changes: 14 additions & 0 deletions packages/sample4/main/scene-map.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { RpgSceneMapHooks, RpgSceneMap } from '@rpgjs/client'

const sceneMap: RpgSceneMapHooks = {
onAfterLoading(scene: RpgSceneMap) {
scene.viewport?.setZoom(1.4);

scene.on('click', (e) => {
console.log('click', e);
scene.game.clientEngine.socket.emit('shoot-bullet', e);
});
}
}

export default sceneMap;
42 changes: 42 additions & 0 deletions packages/sample4/main/spritesheets/bullets/BulletSpritesheets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { Animation, Direction, Spritesheet } from '@rpgjs/client'
import { FrameOptions } from '@rpgjs/client/lib/Sprite/Spritesheet';

const getAnchor = (direction: Direction) => {
switch (direction) {
case Direction.Down:
return [0, -1];
case Direction.Up:
return [0, 2];
case Direction.Left:
return [0, 0];
case Direction.Right:
return [2, 0];
}
}

const anim = (direction: Direction, angle: number, rotation: number): any[][] => {
const animation: FrameOptions[] = [{
time: 0,
frameX: 0,
frameY: 0,
angle: angle,
anchor: getAnchor(direction),
}];

return [animation];
}

@Spritesheet({
framesWidth: 1,
framesHeight: 1,
textures: {
[Animation.Walk]: {
animations: (direction, angle, rotation) => anim(direction, angle, rotation),
},
[Animation.Stand]: {
animations: (direction, angle, rotation) => anim(direction, angle, rotation),
},
},
})
export default class BulletSpritesheets {
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading