javascript - Phaser 3 如何在两个不涉及玩家的物体之间发生碰撞时访问并影响玩家

标签 javascript typescript phaser-framework

我正在尝试在 Phaser 3 中创建一个抓钩。我可以成功地以远离玩家的向上 Angular 射击抓钩。当抓钩到达目的地时,我需要让玩家向抓钩移动。问题是我无法访问 Spawner.ts 文件中的玩家对象来移动玩家对象。如果我可以访问玩家对象,我可以设置抓钩和关卡碰撞时的速度。

这是 Spawner.ts 文件中的相关代码:

import { Actor } from "./Actor";
import { Level } from "./Level";
import { Layer } from "./shared";
import { Projectile } from "./Projectile/Projectile";
import { GameScene } from "../scenes/GameScene";
import { Player } from "./Player";
import { GrapplingHook } from "./GrapplingHook";

interface ActorConstructor {
    new (scene: Phaser.Scene, x: number, y: number): Actor;
}

interface GrapplingHookContructor {
    new (scene: GameScene, x: number, y: number): GrapplingHook;
}

export class Spawner {
    private projectiles: Phaser.GameObjects.Group;
    private actors: Phaser.GameObjects.Group;
    private grapplinghooks: Phaser.GameObjects.Group;

    constructor(private scene: GameScene, private level: Level) {
        this.actors = this.scene.add.group();
        this.setUpGrapplingHooks();
    }

    spawnDynamic<T extends ActorConstructor>(
        layer: Layer,
        type: T
    ): Phaser.GameObjects.Group {
        const objects = this.level.getObjects(layer);
        const instances = objects.map((e) => new type(this.scene, e.x, e.y));
        const group = this.scene.add.group(instances);
        this.level.addGroundCollision(group);
        return group;
    }

    spawnPlayer(layer: Layer): Player {
        const player = this.spawnDynamic(
            layer,
            Player
        ).getChildren()[0] as Player;

        this.actors.add(player);

        return player;
    }

    spawnGrapplingHook<T extends GrapplingHookContructor>(
        type: T,
        x: number,
        y: number,
        xVelocity = 0,
        yVelocity = 0
    ): void {
        const grapplinghook = new type(this.scene, x, y);
        this.grapplinghooks.add(grapplinghook);
        grapplinghook.body.setVelocity(xVelocity, yVelocity);
        grapplinghook.body.setCollideWorldBounds(true, undefined, undefined, true);
    }

    destroyGrapplingHooks() {
        this.grapplinghooks.getChildren().map(child => child.destroy());
    }

    private getObjectData(
        object: Phaser.Types.Tilemaps.TiledObject
    ): Record<string, unknown> {
        const props = object.properties as unknown;
        const data: Record<string, unknown> = {};

        if (props instanceof Array) {
            props.forEach((p: { name: string; value: unknown }) => {
                data[p.name] = p.value;
            });
        }

        return data;
    }

    private setUpGrapplingHooks() {
        this.grapplinghooks = this.scene.add.group();
        this.grapplinghooks.runChildUpdate = true;
        this.level.addGroundCollision(this.grapplinghooks, (grapplinghook) =>
            (grapplinghook as GrapplingHook).onLevelCollide()
        );

        this.scene.physics.add.collider(
            this.grapplinghooks,
            this.level.getLayer(Layer.Bricks),
            (grapplinghook) => (grapplinghook as Projectile).onLevelCollide()
        );

        this.scene.physics.add.collider(
            this.grapplinghooks,
            this.actors,
            (grapplinghook, entity) => {
                (grapplinghook as Projectile).onCollide(entity as Actor);
            }
        );
    }
}

这是Player.ts文件中的相关代码:

import { TILE_WIDTH } from "./shared";
import type { GameScene } from "../scenes/GameScene";
import { Actor } from "./Actor";
import { Assets } from "./shared";
import { GrapplingHook } from "./GrapplingHook";

interface Controls {
    Left: Phaser.Input.Keyboard.Key;
    Right: Phaser.Input.Keyboard.Key;
    Jump: Phaser.Input.Keyboard.Key;
    ThrowGrapplingHook: Phaser.Input.Keyboard.Key;
}

export class Player extends Actor {
    protected readonly totalHitPoints = 3;
    protected readonly immunityAfterDamageTime: number = 1000;

    private controls: Controls;

    constructor(scene: GameScene, x: number, y: number) {
        super(scene, x, y, Assets[Assets.Player], 1);

        // controls
        this.controls = {
            Left: scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.A),
            Right: scene.input.keyboard.addKey(
                Phaser.Input.Keyboard.KeyCodes.D
            ),
            Jump: scene.input.keyboard.addKey(
                Phaser.Input.Keyboard.KeyCodes.SPACE
            ),
            ThrowGrapplingHook: scene.input.keyboard.addKey(
                Phaser.Input.Keyboard.KeyCodes.H
            )
        };
    }

    /**
     * Throws a projectile at a given angle and force
     * @param time The current game time
     * @param angle The angle to throw the projectile; Should be a value between -90 (straight down) and 90 (straight up)
     * @param force What velocity to throw the projectile at
     */

    protected throwGrapplingHook(time: number, angle: number, force: number) {
        if (angle > 90 || angle < -90) {
            throw `throwProjectile(angle) must be between -90 and 90; current value: ${angle}`;
        }

        this.lastProjectileThrowTime = time;

        let x = this.body.x;
        if (this.flipX) {
            x = x + TILE_WIDTH;
        }

        // calculate the x and y force based on angle and total force
        // angle: 0 -> x velocity at 100%, y at 0%
        // angle: 90 -> x velocity at 0%, y at 100%

        const percentYForce = angle / 90;
        const yVelocity = force * percentYForce * -1;
        let xVelocity = force - Math.abs(yVelocity);

        if (this.body.velocity.x < 0) {
            xVelocity *= -1;
        }

        this.scene.spawner.spawnGrapplingHook(
            GrapplingHook,
            x,
            this.body.y,
            xVelocity,
            yVelocity
        );
    }

    protected onUpdate(): void {
        if (!this.active) {
            return;
        }
    }
}

这是 GrapplingHook.ts 文件中的相关代码:

import type { Actor } from "./Actor";
import { Assets } from "./shared";

export class GrapplingHook extends Phaser.Physics.Arcade.Sprite {
    declare body: Phaser.Physics.Arcade.Body;

    constructor(scene: Phaser.Scene, x: number, y: number) {
        super(scene, x, y, Assets[Assets.Projectiles], 1);

        scene.add.existing(this);
        scene.physics.add.existing(this);
        this.body.setAllowGravity(false);

        this.body
            .setSize(this.body.width, this.body.height - 20, true)
            .updateCenter()
    }

    onCollide(target: Actor): void {
        this.destroy();
    }

    onLevelCollide(): void {
        this.setVelocity(0,0);
    }

    update(): void {
        this.flipX = this.body.velocity.x >= 0
    }
}

现在,代码成功抛出了抓钩,但在玩家与关卡碰撞后我实际上无法移动玩家。我什至没有研究如何将玩家移向实际碰撞的抓钩,作为概念证明,我只想在碰撞发生时插入玩家向前。 我的直觉是将 GrapplingHook.ts 中的 onLevelCollide 函数更改为:

onLevelCollide(player: Player): void {
        player.setVelocityX(100);
        this.setVelocity(0,0);
    }

然后在 Spawner.ts 中调用时将玩家对象添加到 onLevelCollide() 中,但我似乎无法访问 Spawner 中的玩家。 ts。我如何将玩家对象传递到 onLevelCollide 或以其他方式解决此问题?

我再次尝试移动抓钩与关卡碰撞的玩家。如果您需要我发布更多代码或进行澄清,请告诉我。

最佳答案

重构 GrapplingHook 构造函数以接受玩家

Spawner.ts

interface GrapplingHookContructor {
    // refactor the constructor to accept a player as well
    new (scene: GameScene, player: Player, x: number, y: number): GrapplingHook;
}

GrapplingHook.ts 中执行相同操作,添加一个玩家属性并从构造函数参数为其分配玩家

export class GrapplingHook extends Phaser.Physics.Arcade.Sprite {
    declare body: Phaser.Physics.Arcade.Body;

    private player: Player; // add this

    // refactor the constructor to accept a player as well
    constructor(scene: Phaser.Scene, player: Player, x: number, y: number) {
        super(scene, x, y, Assets[Assets.Projectiles], 1);
        
        this.player = player; // add this

重构 spawnGrapplingHook 调用以接受玩家并将玩家传递给 GrapplingHook 构造函数

spawnGrapplingHook<T extends GrapplingHookContructor>(
        type: T,
        player: Player, // add this
        x: number,
        y: number,
        xVelocity = 0,
        yVelocity = 0
    ): void {
        // pass the player as a parameter
        const grapplinghook = new type(this.scene, player, x, y);
        // ...
    }

GrapplingHook.ts

onLevelCollide(): void {
    // do the math here to move the player
    this.player.setVelocityX(100);
    this.setVelocity(0,0);
}

另一个可能对您有用的选项是在 Spawner.ts 中保留对播放器的引用,在调用 spawnPlayer(layer: Layer): Player 时设置它。然后使用 this.scene.spawner.player 在您需要的任何地方访问它?

关于javascript - Phaser 3 如何在两个不涉及玩家的物体之间发生碰撞时访问并影响玩家,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70794542/

相关文章:

javascript - Phaser.js : How to get or remove all sprites in one scene

javascript - 选择的 jQuery 插件不起作用。

module - 在 TypeScript 0.9 中结合外部模块和内部模块

javascript - 是否可以使用 Web Audio Api 发布处理 HTML5 视频元素音频输出?

jquery - 使用Typescript(Angular 8)一个接一个地旋转木马

typescript - 将键值对接口(interface)列表转换为单个映射类型

javascript - Phaser 3 与 es6 类

vuejs2 - 这是在 Vue.js 组件中设置 id 的正确方法吗?

javascript - 如何在 JavaScript 中使用 CSS 过滤器?

javascript - 字符串作为对对象变量的对象引用