本系列教程大概会有三篇
- 地图篇:基于
图像
的路墙系统(GraphicBased
)
- 移动篇:角色和地图相对移动
- 互动篇:和地图上的元素互动
会不会写完看大家的反响:p
地图篇
地图决定了玩家所操控的游戏角色可以移动的范围和区域,游戏开发中有多种方式来实现地图。但无论是3D游戏,还是2D游戏,地图系统的底层一般都是基于2d图形。以风来之国游戏中下面这个场景为例
红色画笔是玩家操作角色允许移动的区域,我们要如何去实现呢?首先先把这个场景地图分层
- 地板层
角色永远在地板之上
- 建筑层
会根据角色的位置相互覆盖
因为我没有风来之国的美术资源
所以我这个灵堂级画师
....
手绘
了一个游戏地图
和原版可谓是毫无分别
搭架子
这里我们会使用 PIXI.js
引擎来搭建这个游戏系统,需要注意的是,我希望大家明白,文章重点在说原理,不是非得要用某个指定的引擎。
下面的代码可让玩家控制人
上下左右移动。
<script src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/5.1.3/pixi.min.js"></script> <script type="text/javascript"> //初始化PIXI const app = new PIXI.Application({ backgroundColor:0xffffff }); window.onload=function(){ //将PIXI的渲染视图添加到网页body中 document.body.appendChild(app.view); app.renderer.autoResize = true; app.renderer.resize(window.innerWidth, window.innerHeight); //加载图片资源 app.loader. add('player', 'player.png'). add('floor','floor.jpg'). load((loader, resources) => { //将图片资源添加到场景 const floor = new PIXI.Sprite(resources.floor.texture); app.stage.addChild(floor); const player = new PIXI.Sprite(resources.player.texture); app.stage.addChild(player); //将玩家图形的原点设置为中心点 player.anchor.x = 0.5; player.anchor.y = 0.5; app.ticker.add(() => { //帧同步事件的回调 }); //上下左右键按下后角色移动 window.onkeydown=function (e) { if(e.keyCode==37) { player.x-=2; }else if(e.keyCode==39){ player.x+=2; } if(e.keyCode==38){ player.y-=2; }else if(e.keyCode==40){ player.y+=2; } } }); } </script>
TADA! 现在我们的角色动起来了
现在这个移动不是很流畅,并且无法响应两个按键同时按下,也就是无法进行8向移动(↖↑↗ ←→ ↙↓↘
),我们如此调整一下代码
app.ticker.add(() => { var nextX = player.x+moveDelta.x; var nextY = player.y+moveDelta.y; player.x=nextX; player.y=nextY; }); window.onkeyup=function(e){ if(e.keyCode==37||e.keyCode==39) { moveDelta.x=0; }else if(e.keyCode==38||e.keyCode==40){ moveDelta.y=0; } } window.onkeydown=function (e) { if(e.keyCode==37) { moveDelta.x=-2; }else if(e.keyCode==39){ moveDelta.x=2; } if(e.keyCode==38){ moveDelta.y=-2; }else if(e.keyCode==40){ moveDelta.y=2; } }
丝滑多了,并且是8向移动的!
路墙系统
有些地方角色能去,有些地方不能去,这个要如何实现呢?在大部分游戏中,大致可以分为两种实现方式,其一是基于瓦片来实现,就是把地图分为不同的格子,用二维数组来决定哪些地方是路,哪些地方是墙,比如下面这个二维数组,1代表墙,0代表路。如果用相同尺寸的地板素材,可以很方便的建立路墙系统。
roadarea=[ 1,1,1,1,1,1,1,1, 1,0,0,0,0,0,0,1, 1,0,0,0,0,0,0,1, 1,1,1,1,1,1,1,1 ]
但这种方式比较适合由固定尺寸的瓦片图拼接的游戏,我将另起一系列文章来分享瓦片游戏系统
的搭建。在本文中,我们使用基于图像的路墙系统GraphicBased
。首先我们需要一个路墙
区域标识图,大家注意看下图,这里不能行走的区域用红色来表示。
相信大家已经明白我们要怎么做了!
我们只需要预判移动的目标位置下的像素是否为红色就不让继续移动...
下面我们来看看代码,注意,为了代码不过于长,这里贴的代码都是片段,要仔细阅读注释来理解或者对照我提供的源码
//将上图中的路墙标识图加载到系统中 add('roadarea','roadarea.jpg') const roadarea = new PIXI.Sprite(resources.roadarea.texture); app.stage.addChild(roadarea); //获得路墙标识图的全部像素数组 //格式[r,g,b,a],所以数组长度为路墙标识图的 宽x高x4 roadareaPixels = app.renderer.extract.pixels(roadarea); //判断x,y坐标下的像素是否为墙体的标识颜色 function isHitWall(x,y){ var width = 1728;//路墙标识图宽度 var position = (width * y + x) * 4;//还原像素序号 //取出颜色值r,g,b,a var r=roadareaPixels[position],g=roadareaPixels[position+1],b=roadareaPixels[position+2],a=roadareaPixels[position+3]; //jpeg格式输出后颜色会略有损失,这里根据实际调整 //就是判断x,y坐标下的颜色是否为红色 if(r==255&&g<50){ console.log("hit the wall"); return true; }else{ return false; } } app.ticker.add(() => { var nextX = player.x+moveDelta.x; var nextY = player.y+moveDelta.y; //只有在移动的目标坐标下不是墙体颜色时才允许移动 if(!isHitWall(player.x,nextY)){ player.y=nextY; } if(!isHitWall(nextX,player.y)){ player.x=nextX; } });
大家仔细看GIF
,遇到墙时,调试信息输出了hit the wall
这部分代码有看不到明白的,可以仔细看看这篇文章《# 使用Javascript制作BadApple字符画视频》,其中使用的技术原理是一样的。
细节优化
现在判定的x,y来自角色的中心点,我们把角色的半径算上。并且隐藏路墙标识图
,我们不需要看见它。
// app.stage.addChild(roadarea); //在判定移动的目标位置时加上操控角色的半径,这里差不多是45 if(!isHitWall(player.x,nextY+45*moveDelta.y)){ player.y=nextY; } if(!isHitWall(nextX+45*moveDelta.x,player.y)){ player.x=nextX; }
本文就此结束,下一篇我将给大家讲讲
- 移动篇:角色和地图相对移动