Phaser教程:创建实时策略游戏基础
你是否想开发一款像《星际争霸》或《帝国时代》那样的实时策略游戏(Real-Time Strategy, RTS)?但苦于复杂的游戏逻辑和地图系统难以实现?本文将带你使用Phaser框架,从零开始搭建一个RTS游戏的核心基础,包括地图创建、单位移动和资源管理系统。读完本文后,你将掌握:
- 使用瓦片地图(Tilemap)构建游戏世界
- 实现单位的点击选择与路径移动
- 设计资源收集与基地建设机制
- 处理多单位控制与碰撞检测
准备工作
Phaser是一款快速、免费且有趣的2D游戏框架,专为HTML5游戏开发设计,支持Canvas和WebGL渲染。首先确保已通过以下方式获取Phaser:
<!-- 使用国内CDN引入Phaser -->
<script src="https://cdn.jsdelivr.net/npm/phaser@3.85.0/dist/phaser.min.js"></script>
如果你倾向于本地开发,可以通过Git克隆项目仓库:
git clone https://gitcode.com/gh_mirrors/pha/phaser.git
项目核心文件结构如下,我们将重点关注标记的模块:
src/
├── core/ # 游戏核心系统 [src/core/Game.js](https://link.gitcode.com/i/5a4a94f2973ecfbdce5b17c250244fe3)
├── tilemaps/ # 瓦片地图系统 [src/tilemaps/](https://link.gitcode.com/i/fd9b628f43613e6bd909c56652641f7b)
├── physics/ # 物理引擎 [src/physics/arcade/ArcadePhysics.js](https://link.gitcode.com/i/ddfb9afd4f685ce283ec47de1e808620)
├── input/ # 输入系统
└── gameobjects/ # 游戏对象系统
构建游戏地图
RTS游戏的核心是一张可交互的地图,Phaser的瓦片地图(Tilemap)系统非常适合构建这类场景。以下是创建RTS地图的完整步骤:
1. 初始化游戏与地图
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
physics: {
default: 'arcade',
arcade: {
debug: false, // 开发时可设为true显示碰撞体
gravity: { y: 0 }
}
},
scene: {
preload: preload,
create: create,
update: update
}
};
const game = new Phaser.Game(config);
let map;
let units = [];
let selectedUnit = null;
2. 加载地图资源
function preload() {
// 加载瓦片集图像和地图数据
this.load.image('terrain', 'assets/terrain.png');
this.load.tilemapTiledJSON('rtsMap', 'assets/rts-map.json');
// 加载单位图像
this.load.spritesheet('units', 'assets/units.png', { frameWidth: 32, frameHeight: 32 });
}
3. 创建瓦片地图
使用Tiled地图编辑器创建地图后,通过Phaser的Tilemap系统加载:
function create() {
// 创建地图
map = this.make.tilemap({ key: 'rtsMap' });
// 添加瓦片集
const tileset = map.addTilesetImage('terrain', 'terrain');
// 创建图层 - 注意图层名称需与Tiled中一致
const groundLayer = map.createLayer('ground', tileset, 0, 0);
const resourcesLayer = map.createLayer('resources', tileset, 0, 0);
const buildingsLayer = map.createLayer('buildings', tileset, 0, 0);
// 设置碰撞区域 - 标记不可通行的瓦片
map.setCollisionByProperty({ collides: true }, true, groundLayer);
// 创建物理碰撞组
unitsGroup = this.physics.add.group();
// 添加初始单位
addUnit(400, 300, 'worker');
}
实现单位控制
RTS游戏的核心交互是"点击-移动"模式,我们需要实现以下功能:
1. 单位选择机制
function create() {
// ... 前面的地图创建代码 ...
// 监听鼠标点击
this.input.on('pointerdown', (pointer) => {
// 检查是否点击了单位
const clickedUnit = getUnitAtPosition(pointer.worldX, pointer.worldY);
if (clickedUnit) {
selectUnit(clickedUnit);
} else {
// 如果已选择单位且点击空地,则移动
if (selectedUnit) {
moveUnitTo(selectedUnit, pointer.worldX, pointer.worldY);
}
}
});
}
// 选择单位
function selectUnit(unit) {
// 取消之前选中单位的高亮
if (selectedUnit) {
selectedUnit.setTint(0xffffff); // 恢复原色
}
selectedUnit = unit;
unit.setTint(0x00ff00); // 绿色高亮选中单位
// 显示选中单位的信息面板
showUnitInfo(unit);
}
2. 路径移动实现
Phaser的物理系统提供了moveTo方法,可以轻松实现单位向目标点移动:
function moveUnitTo(unit, x, y) {
// 停止当前移动
unit.body.velocity.set(0);
// 使用物理系统移动单位 [src/physics/arcade/ArcadePhysics.js](https://link.gitcode.com/i/ddfb9afd4f685ce283ec47de1e808620)
this.physics.moveToObject(unit, {x, y}, 100); // 速度100像素/秒
// 记录目标位置用于动画更新
unit.targetX = x;
unit.targetY = y;
}
// 更新单位动画和移动状态
function update(time, delta) {
units.forEach(unit => {
if (unit.targetX !== null && unit.targetY !== null) {
// 检查是否到达目标
const distance = Phaser.Math.Distance.Between(
unit.x, unit.y, unit.targetX, unit.targetY
);
if (distance < 10) { // 到达目标附近
unit.body.velocity.set(0);
unit.targetX = null;
unit.targetY = null;
unit.anims.play('idle', true);
} else {
// 更新移动动画
updateMovementAnimation(unit);
}
}
});
}
资源收集系统
RTS游戏中资源收集是核心玩法之一,我们将实现一个简单的矿物收集系统:
1. 标记资源点
在Tiled地图编辑器中,给资源瓦片添加resource属性(如gold或wood),然后在代码中检测:
function create() {
// ... 前面的代码 ...
// 获取所有资源瓦片 [src/tilemaps/Tilemap.js](https://link.gitcode.com/i/04783ceddebc020baf844d0cb556cf87)
const resourceTiles = map.filterTiles(tile => tile.properties.resource);
// 为每个资源点创建交互区域
resourceTiles.forEach(tile => {
const resource = this.add.rectangle(
tile.x * map.tileWidth + map.tileWidth/2,
tile.y * map.tileHeight + map.tileHeight/2,
map.tileWidth,
map.tileHeight
).setOrigin(0.5);
this.physics.add.existing(resource, true); // 静态物体
resource.resourceType = tile.properties.resource;
resource.amount = 100; // 资源数量
// 添加碰撞检测
this.physics.add.overlap(unitsGroup, resource, collectResource);
});
}
2. 资源收集逻辑
function collectResource(unit, resource) {
if (unit.type === 'worker' && resource.amount > 0) {
// 减少资源数量
resource.amount -= 10;
// 增加玩家资源
playerResources[resource.resourceType] += 10;
// 更新UI显示
updateResourceUI();
// 如果资源耗尽,从地图中移除
if (resource.amount <= 0) {
resource.destroy();
// 更新瓦片显示
const tile = map.getTileAtWorldXY(resource.x, resource.y);
if (tile) {
tile.index = -1; // 清除瓦片
}
}
}
}
建筑系统基础
RTS游戏的另一核心是基地建设,以下是实现建筑放置的基础代码:
// 建筑按钮点击事件
function onBuildingButtonClicked(buildingType) {
if (hasEnoughResources(buildingType)) {
// 进入建筑放置模式
buildingMode = buildingType;
showBuildPreview();
}
}
// 显示建筑预览
function showBuildPreview() {
// 创建半透明预览精灵
buildPreview = this.add.sprite(0, 0, 'buildings', buildingType)
.setAlpha(0.5)
.setOrigin(0.5);
// 跟随鼠标移动
this.input.on('pointermove', (pointer) => {
if (buildingMode) {
// 对齐到网格
const gridX = Math.floor(pointer.worldX / map.tileWidth) * map.tileWidth + map.tileWidth/2;
const gridY = Math.floor(pointer.worldY / map.tileHeight) * map.tileHeight + map.tileHeight/2;
buildPreview.setPosition(gridX, gridY);
// 检查是否可以放置
if (canPlaceBuilding(gridX, gridY, buildingType)) {
buildPreview.setTint(0x00ff00); // 可以放置
} else {
buildPreview.setTint(0xff0000); // 不可放置
}
}
});
// 确认放置
this.input.on('pointerdown', (pointer) => {
if (buildingMode && canPlaceBuilding(pointer.worldX, pointer.worldY, buildingType)) {
placeBuilding(pointer.worldX, pointer.worldY, buildingType);
buildPreview.destroy();
buildingMode = null;
}
});
}
碰撞与路径优化
在RTS游戏中,单位之间和单位与建筑之间的碰撞处理非常重要:
function create() {
// ... 前面的代码 ...
// 单位之间的碰撞
this.physics.add.collider(unitsGroup, unitsGroup);
// 单位与建筑的碰撞
this.physics.add.collider(unitsGroup, buildingsGroup);
// 启用瓦片地图碰撞 [src/tilemaps/components/SetCollision.js](https://link.gitcode.com/i/f459d1c72193f8d26dac3e3126c527df)
this.physics.add.collider(unitsGroup, groundLayer);
}
对于更复杂的路径寻找,我们可以实现一个简单的A*算法或使用Phaser的路径插件。以下是简化版路径计算:
function findPath(startX, startY, endX, endY) {
// 将像素坐标转换为瓦片坐标 [src/tilemaps/components/WorldToTileXY.js](https://link.gitcode.com/i/2bb0d79969ac6847cd652a10fe715cc2)
const startTile = map.worldToTileXY(startX, startY);
const endTile = map.worldToTileXY(endX, endY);
// 简单的直线路径(实际项目中应替换为A*算法)
const path = [];
// 添加中间点
path.push({x: startX, y: startY});
// 添加目标点
path.push({x: endX, y: endY});
return path;
}
总结与进阶方向
通过本文,你已经掌握了RTS游戏的核心基础:
- 使用瓦片地图创建游戏世界 src/tilemaps/Tilemap.js
- 实现单位选择与移动控制 src/physics/arcade/ArcadePhysics.js
- 资源收集系统设计
- 基础建筑放置与碰撞检测
进阶方向:
- 完善AI系统:实现敌方单位的巡逻、攻击和资源收集逻辑
- 科技树系统:添加单位升级和建筑解锁机制
- 多人游戏:通过WebSocket实现实时多人对战
- 高级视觉效果:添加粒子系统和光影效果 src/fx/
Phaser提供了丰富的功能和良好的扩展性,你可以通过查阅官方文档和源码进一步探索更多可能性。祝你开发出属于自己的RTS游戏杰作!
点赞收藏本文,关注后续教程,我们将深入探讨RTS游戏的高级AI和网络同步技术!
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



