You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Phaser 3:实时输出敌人地形瓦片属性及切换动画问题求助

问题描述

我正在开发一款俯视视角RPG游戏,需求是实时通过console.log()输出敌人精灵所在的瓦片或地形属性,以此切换敌人在陆地或水中的动画。尝试使用console.log(this.streetLayer.getTileAtWorldXY(this.enemy.x, this.enemy.y, true));,但在update()方法中地图对象未定义,现询问是否有更简便的实现方式。

完整代码

import "./style.css";
import Phaser from "phaser";

const sizes = {
  width: 960,
  height: 640,
};
let last_direction;
let enemy_terrain;


class GameScene extends Phaser.Scene {
  constructor() {
    super("scene-game");
  }

  preload() {
    // Load spritesheet for the protagonist
    this.load.spritesheet('protagonist', '/assets/mainCH.png', { frameWidth: 100, frameHeight: 100 });
    this.load.spritesheet('enemy', '/assets/waterspirit.png', { frameWidth: 150, frameHeight: 150 });
    
  
    // Load tileset and tilemap
    this.load.tilemapTiledJSON('map', '/assets/maps/testmap.json');

    this.load.image('tiles', 'assets/maps/terrain.png');
  }

  create() {
    console.log('create method executed');
    this.protagonist = this.physics.add.sprite(200, 550, 'protagonist');
    this.enemy = this.physics.add.sprite(200, 100, 'enemy');
    
    // Set up properties for the enemy
    this.enemy.speed = 100; // Adjust the speed as needed
    this.protagonist.setDepth(1);
    this.enemy.setDepth(1);
    const map = this.make.tilemap({ key: 'map' });



    // Add tilesets for each layer
    const Tileset = map.addTilesetImage('test-tiles', 'tiles');
  
    
    // Create layers from tilemap data
    const streetLayer = map.createLayer('street', Tileset, 0, 0); 
    const waterLayer = map.createLayer('water', Tileset, 0, 0); 

    console.log('Street Layer:', streetLayer); // Log the street layer object to check if it's properly created
    console.log('Water Layer:', waterLayer); // Log the street layer object to check if it's properly created
  
    // Adjust the depth of layers as needed
    streetLayer.setDepth(0);
    waterLayer.setDepth(0);
    map.setCollisionBetween(188, 188, true, waterLayer);
    map.setCollisionBetween(0, 0, true, streetLayer);

 /////////////////////////BUNCH OF ANIMATIONS START///////////////////////////////////
 /////////////////////////BUNCH OF ANIMATIONS END///////////////////////////////////
    this.cursors = this.input.keyboard.createCursorKeys();
    // Enable physics for the protagonist
    this.physics.world.setBounds(0, 0, map.widthInPixels, map.heightInPixels);
    this.physics.add.collider(this.protagonist, streetLayer);
    this.physics.add.collider(this.enemy, streetLayer);
 
  
    if(this.enemy.y >= 350){
        enemy_terrain = "street"
      this.enemy.play('enemy_move_street');
  
     } else {
        enemy_terrain = "water"
    
     }
 
 // Play the appropriate animation based on the collision result
 if (enemy_terrain == "street") {
     this.enemy.play('enemy_move_street');
     console.log("enemy on street");
  
 } else {
     this.enemy.play('enemy_move_water');
     console.log("enemy on water");
 }


 
  }

  update() {
    // Reset velocity
    this.protagonist.setVelocity(0);
   
    // Handle keyboard input
        XXXX
    this.physics.moveToObject(this.enemy, this.protagonist, this.enemy.speed);
 // Call updateInfo method
    this.updateInfo();
  }
  updateInfo() {
    let timedEvent = this.time.addEvent({ 
      delay: 1000, // once every second = 1000ms
      callback: () => console.log(this.waterLayer.getTileAtWorldXY(this.enemy.x, this.enemy.y, true)), 
      loop: true });
}
}


const config = {
  type: Phaser.WEBGL,
  width: sizes.width,
  height: sizes.height,
  canvas: gameCanvas,
  physics: {
    default: "arcade",
    arcade: {
      debug: true, // Set to true to enable debugging (displays collision bodies)
    },
  },
  scene: [GameScene],
};

const game = new Phaser.Game(config);
解决方案

1. 修复图层作用域问题

你在create方法里用const定义的streetLayer和waterLayer是局部变量,update方法无法访问。改成赋值给场景实例属性,就能全局调用:

// 在create方法里替换图层创建代码
this.streetLayer = map.createLayer('street', Tileset, 0, 0); 
this.waterLayer = map.createLayer('water', Tileset, 0, 0);

2. 优化地形检测逻辑

不要在updateInfo里重复创建定时事件(每次update调用都会新建一个,导致重复输出),直接在update里控制检测频率:

// 在场景类中添加属性记录上次检测时间
lastTerrainCheck = 0;

update() {
    // ... 其他原有代码
    this.physics.moveToObject(this.enemy, this.protagonist, this.enemy.speed);
    
    // 每100ms检测一次地形(可根据需求调整间隔)
    const now = this.time.now;
    if (now - this.lastTerrainCheck > 100) {
        this.checkEnemyTerrain();
        this.lastTerrainCheck = now;
    }
}

checkEnemyTerrain() {
    // 获取敌人所在位置的瓦片
    const waterTile = this.waterLayer.getTileAtWorldXY(this.enemy.x, this.enemy.y, true);
    const streetTile = this.streetLayer.getTileAtWorldXY(this.enemy.x, this.enemy.y, true);
    
    let currentTerrain;
    // 根据你设置的碰撞瓦片ID判断地形
    if (waterTile && waterTile.index === 188) {
        currentTerrain = "water";
    } else if (streetTile && streetTile.index === 0) {
        currentTerrain = "street";
    }
    
    console.log(`Enemy is on: ${currentTerrain}`);
    
    // 仅当地形变化时切换动画,避免重复播放
    if (currentTerrain !== enemy_terrain) {
        enemy_terrain = currentTerrain;
        this.enemy.play(`enemy_move_${currentTerrain}`, true);
    }
}

3. 额外规范优化

  • 把全局变量enemy_terrain改成场景实例属性this.enemyTerrain,避免全局污染,更符合代码规范。
  • 动画播放时添加true参数,确保动画能正确切换或重启。

内容的提问来源于stack exchange,提问作者Aftersun Lotion

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 12:28:13