代码之家  ›  专栏  ›  技术社区  ›  ChristianOConnor

Phaser3如何在两个不涉及玩家的物体之间发生碰撞时访问和影响玩家

  •  0
  • ChristianOConnor  · 技术社区  · 3 年前

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

    这是中的相关代码 繁殖者.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
        }
    }
    

    现在,代码成功地抛出了抓钩,但在玩家与关卡碰撞后,我实际上无法移动玩家。我甚至还没有研究过如何移动球员 朝着 实际上发生碰撞的抓钩,作为概念的证明,我只想在碰撞发生时推动玩家前进。 我的直觉是改变 onLevelCollide 中的函数 抓钩.ts 至:

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

    然后将玩家对象添加到 onLevelCollide() 当它被调用时 繁殖者.ts 但我似乎无法访问中的播放器 繁殖者.ts .我该如何将玩家对象传递到onLevelCollide中,或者可能以其他方式解决此问题?

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

    0 回复  |  直到 3 年前
        1
  •  2
  •   Ma3x    3 年前

    重构 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 同样地,添加一个player属性,并从构造函数参数中为其分配player

    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 呼叫接受玩家并将玩家传给 抓钩 构造函数

    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);
            // ...
        }
    

    在里面 抓钩.ts

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

    另一个可能对你有用的选择是在中保留对玩家的引用 繁殖者.ts ,设置为 spawnPlayer(layer: Layer): Player 被调用。然后使用 this.scene.spawner.player 在其他需要的地方访问它?

        2
  •  1
  •   winner_joiner    3 年前

    只是因为我感兴趣,我自己会如何处理这个问题,我适应了 (我把大部分东西都去掉了,其实并不需要) 这个相位器的例子( First Game )以配合抓钩技工。

    我只是分享一下,因为这是一个涵盖关键任务的小示例:

    • 射勾手
    • 在之前/期间/之后管理速度 挂钩动作
    • 将球员拉向勾手

    是的,它只是没有类(和…)的javascript,但它仍然很好地说明了要点(我认为)。

    控件:

    • 鼠标左键:拍摄“挂钩”
    • 左边 / 正确的 :移动/行走播放器(动画已删除)
    • 移位 收回抓钩

    var config = {
            type: Phaser.AUTO,
            width: 400,
            height: 300,
            physics: {
                default: 'arcade',
                arcade: {
                    gravity: { y: 150 }
                }
            },
            scene: {
                preload: preload,
                create: create,
                update: update
            }
        };
    
        var player;
        var platforms;
        var cursors;
    
        // basic Hook Object
        var hook = {
            isPulling: false,
            isAttached: false,
            speed: 500,
            set display(value){
              this.gameObject.visible = value;
              this.ropeGameObject.visible = value;
            }
        }
    
        var game = new Phaser.Game(config);
    
        function preload ()
        {
            this.load.image('ground', 'https://labs.phaser.io/src/games/firstgame/assets/platform.png');
            this.load.image('star', 'https://labs.phaser.io/src/games/firstgame/assets/star.png');
            this.load.spritesheet('dude', 'https://labs.phaser.io/src/games/firstgame/assets/dude.png', { frameWidth: 32, frameHeight: 48 });
        }
    
        function create ()
        {
        
            let txt = this.add.text(0, 20, '> click mouse to shoot hook\n\t>> press "shift" to pull');
            platforms = this.physics.add.staticGroup();
            platforms.create(200, 310, 'ground');
            platforms.create(200, -10, 'ground');
            platforms.create(400, 100, 'ground').setScale(.5).refreshBody();
            platforms.create(50, 175, 'ground').setScale(.5).refreshBody();
            
            // Setup Hook Rope
            hook.ropeGameObject = this.add.line(0,0, 0,0, 10,10, 0xff0000).setOrigin(0);
            
            // Setup Hook
            hook.gameObject = this.physics.add.sprite(100, 450, 'star').setScale(.3);
            hook.gameObject.body.allowGravity = false;
            
            // Hide Hook
            hook.display = false;
            
            // Setup Hook Collision
            this.physics.add.collider(hook.gameObject, platforms, grab);
            
            player = this.physics.add.sprite(100, 450, 'dude',4).setScale(.75);
            player.setCollideWorldBounds(true);
            player.setBounce(0.2);
            this.physics.add.collider(player, platforms);
            
            // Setup User Controls
            cursors = this.input.keyboard.createCursorKeys();
            this.input.on('pointerdown', shootHook, this);
        }
    
        function grab(hk, platform){
            hook.gameObject.setVelocity(0);
            hook.isAttached = true;
        }
    
        function shootHook(pointer){
            if(!hook.isPulling){
                let velocity = new Phaser.Math.Vector2(pointer.x - player.x, pointer.y - player.y)
                    .normalize()
                    .scale(hook.speed);
                hook.gameObject.x = player.x;
                hook.gameObject.y = player.y;
                hook.display = true;
                hook.isAttached = false;  
                hook.gameObject.setVelocity(velocity.x, velocity.y);
            }
        }
    
        function updateHookRope(){
            hook.ropeGameObject.setTo(player.x, player.y,hook.gameObject.x,hook.gameObject.y);
        }
    
        function update ()
        {
            updateHookRope();
            if(hook.isAttached && cursors.shift.isDown){
                hook.isPulling = true;
                let pullVelocity = new Phaser.Math.Vector2( hook.gameObject.x - player.x, hook.gameObject.y - player.y)
                    .normalize()
                    .scale(hook.speed / 1.5);
                player.setVelocity(pullVelocity.x, pullVelocity.y);
                
            } else if(hook.isPulling) { // Hook was released so remove it
              if(hook.isAttached){
                 hook.isAttached = false;
                 hook.display = false;
               }
               hook.isPulling = !player.body.touching.down;
            }
    
            if(hook.isPulling){
                return;
            }
    
            if (cursors.left.isDown) {
                player.setVelocityX(-160);
            }
            else if (cursors.right.isDown) {
                player.setVelocityX(160);
            }
            else {
                player.setVelocityX(0);
            }
    
        }
    <script src="//cdn.jsdelivr.net/npm/phaser@3.55.2/dist/phaser.min.js"></script>