12、开源游戏

Wesley13
• 阅读 699

在前面中我们初始化了游戏的资源,这次我们来说下地图的绘制和游戏主循环设计。

地图绘制

    以前说过地图是用tiled画好,导出为图片形式的,所以地图的绘制,就是把这个图片绘制到canvas的过程。这样绘制地图就简单了,使用drawImage方法绘制即可。

    这里有个2问题,1是地图的大小一般肯定是大于canvas的,所以我们只是把地图的一部分绘制到了canvas上,2是地图的移动。1中的地图的复制位置是根据2中地图的移动距离来确定的。我们的思路如下:记录鼠标移动的xy坐标值,然后根据xy值和canvas边缘做比较,当靠近边缘时,我们就移动地图一段距离,重复这个过程,直到地图绘制完。

其实我们上面的思路中,就是在改变drawImage方法的参数过程,那么来看下drawImage方法:

定义和用法

drawImage() 方法绘制一幅图像。

语法

drawImage(image, x, y)
drawImage(image, x, y, width, height)
drawImage(image, sourceX, sourceY, sourceWidth, sourceHeight, destX, destY, destWidth, destHeight)

image

所要绘制的图像。这必须是表示 标记或者屏幕外图像的 Image 对象,或者是 Canvas 元素。

x, y

要绘制的图像的左上角的位置。

width, height

图像所应该绘制的尺寸。指定这些参数使得图像可以缩放。

sourceX, sourceY

图像将要被绘制的区域的左上角。这些整数参数用图像像素来度量。

sourceWidth, sourceHeight

图像所要绘制区域的大小,用图像像素表示。

destX, destY

所要绘制的图像区域的左上角的画布坐标。

destWidth, destHeight

图像区域所要绘制的画布大小。

描述

drawImage() 方法有 3 个变形。第一个变形把整个图像复制到画布,将其放置到指定点的左上角,并且将每个图像像素映射成画布坐标系统的一个单元。第二个变形也把整个图像复制到画布,但是允许您用画布单位来指定想要的图像的宽度和高度。第三个变形则是完全通用的,它允许您指定图像的任何矩形区域并复制它,对画布中的任何位置都可进行任何的缩放。

传递给 drawImage() 方法的图像必须是 Image 对象Canvas 元素。一个 Image 对象能够表示文档中的一个 标记或者使用 Image() 构造函数所创建的一个屏幕外图像。

再看一下这个图:

12、开源游戏

我们使用该方法的最后一种变形,代码如下:

game.bgContext.drawImage(game.taskMapImage,game.offsetX,game.offsetY,game.canvasWidth,game.canvasHeight, 0,0,game.canvasWidth,game.canvasHeight);

我仔细说明一下:

  • bgContext是我们背景canvas的绘图环境,同样我们在初始化时将它保存了起来,这样我们就可以使用2d的绘图环境。代码如下:

    game.bgCanvas = $('#bgcanvas'); game.bgContext = game.bgCanvas.getContext('2d');

  • game.taskMapImage为当前任务的地图,现在所有任务都是一个地图,而且也没有障碍物,我打算在做胡子第二版时用一个框架来实现这个障碍物碰撞检测。

  • offsetX和offsetY,是指地图移动的偏移量,默认未移动时为0。当地图向左侧移动了20像素后,offsetX变为20,同理offsetY也是如此计算。这个20就是我们说的移动一段距离的值。这2个值决定了,我们从地图图片哪开始复制,结合他后面的2个参数,就是我从哪开始复制,复制多宽、多高的图片。

  • game.canvasWidth和game.canvasHeight为背景canvas的宽高,即复制地图中canvas宽高的地图。

  • 0,0为背景canvas左上角的坐标,即将地图图片,从这开始绘制。

  • game.canvasWidth,game.canvasHeight 绘制地图的大小,没说的肯定是背景canvas的宽高了。

那我们怎么判断是否需要移动地图呢?我们根据鼠标的xy位置和一个阀值做比较,即鼠标x值距离背景canvas的左右边界小于阀值像素时,我们移动一次地图,鼠标y同理。

阀值我们为10像素,每次移动20像素的地图距离。代码如下:

if(mouse.x<=10){
 if (game.offsetX>=20){
 game.offsetX -= 20; 
 }else if (game.offsetX>0){
 game.offsetX = 0; 
 
 }
} else if (mouse.x>= game.canvasWidth - 10){
 if (game.offsetX + game.canvasWidth + 20 <= game.currentMapImage.width){
 game.offsetX += 20;
 }else{
 game.offsetX += game.currentMapImage.width-(game.offsetX + game.canvasWidth);
 }
}

下面代码中,我们判断鼠标的x值,如果x距离左侧边界小于等于10时,判断是否已经移动了超过20像素,是我们就减去20,不足时我们恢复成0,同理我们判断鼠标x距离右侧边界时的情况。类似完成鼠标y的判断。

通过上面的代码,当面的条件满足时,我们改变offsetX和offsetY的值,然后即调用drawImage方法绘制新的地图,来实现地图的移动。

game.bgContext.drawImage(game.taskMapImage,game.offsetX,game.offsetY,game.canvasWidth,game.canvasHeight, 0,0,game.canvasWidth,game.canvasHeight);

小节

我们使用鼠标的xy和背景canvas边界做比较,来判断是否需要移动地图,是的话就根据移动的距离,重新绘制地图。这里对鼠标移动的监听没有说明,主要使用jquery事件:

$(document).mousemove(function(e){
        mouse.x = e.pageX;
        mouse.y = e.pageY;
    });

这里还需要减去背景canvas相对左和顶部的距离(如果有的话),这个都不细说。在上面中我们还有一个问题,就是谁来触发这个判断动作,由鼠标事件来做?如果这样这个鼠标事件js就有点不单存了,我想用游戏的主循环触发这个判断,当然不是直接去做,那样更不好。

设计游戏主循环

    在前面时,我们提到过游戏的基本原理就是“绘制 擦除 绘制”循环它。我感觉这个很重要!这个循环控制着或说触发着游戏的一系列事件的产生执行。这个和flash好像啊,如果了解过flash的动画,应该知道弄flash动画,都弄很多小的影片剪辑(movieclip),然后将它们组合或放在主时间轴上,当主时间轴播放时,这些小的影片剪辑也在同时播放着自己的循环(这里可能不太恰当,因为主时间轴可以是停止的,呵呵)。

    那我们可不可以这样想,所谓的游戏开发其实和开发网站、mis系统、bi软件(为啥说他呢,因为我是开发这个的,呵呵)这些系统是一样的,都是把系统分解成许多个小功能,然后组合其中一部分,或根本不用组合,就成了一个系统了。

游戏也是,分解成许多个小功能,尽量模块话(一个稍复杂的游戏不这样弄,真难想象如何控制代码和维护),然后用主循环去组合或执行他们, 不论游戏还是系统,触发动作的一般都是人,如用户点击按钮登录,玩家使用技能,只不过系统中用户触发后一般就是立马执行,而游戏却不同,一般是由循环去执行他,或者说是在下一次循环中执行他。

不知道我说的乱不乱,呵呵。我整理下我的思路:开发一个游戏时,把游戏分解成许多个小功能,然后分别实现他们,我想这里最难的就是如何分解和他们的关系了。(等胡子游戏第一版开发完时,我会发一下代码的类图,到时欢迎大家指出我们的错误和不足呵呵,)。然后主循环中调用它们或调用几个,然后这几个中又调用其他的(他们的关系)。

以前觉得开发游戏时,不知道从何下手,现在我把它和我熟悉的bi开发做了对号或找不同,感觉不再像当初那么迷糊了。下面说主循环的代码设计。

代码设计

    按照前面的废话,主循环只有“绘制 擦除 绘制”就可以了,那就是绘制所有的游戏单元(建筑、车辆、人员),如果是一个没有动画的游戏,我想应该是的,但是胡子有动画,就是精灵图(其实这么说是不准确的,游戏里的一切都精灵),就是每个单元都有自己的一个小的动画循环,如车辆行走,转向和建筑的生产等,如下图的坦克和天电的发电图:

12、开源游戏坦克行走图

12、开源游戏

天电 发电图

就目前来说我们的主循环要干2建事,1绘制所有的游戏单元,2绘制游戏单元的动画。代码如下:

$(window).load(function() {
    game.init();
});

var game = {
    init: function(){
        mouse.init();
    commandbar.init();
    sounds.init();
    data.init();

    ...
    },
    start:function(){
     commandbar.init();
        
     ...
    },
    spiritLoop:function(){
        //调用所有游戏单元的动画方法
        for (var i = 0;i<game.items.length;i++){
            game.items[i].spiritAnimate();
        };
        setInterval(game.spiritLoop,100);
    },    
    drawLoop:function(){
        if (判断是否需要移动地图){
            game.bgContext.drawImage(game.taskMapImage,game.offsetX,game.offsetY,game.canvasWidth,game.canvasHeight, 0,0,game.canvasWidth,game.canvasHeight);
        }
        //调用所有游戏单元的绘制方法
        for (var i = 0;i<game.items.length;i++){
            game.items[i].draw();
        };
        requestAnimationFrame(game.drawLoop);                            
    }
}

这里我们主要设计,不细说代码。

总结

我们按照游戏的原理,来设计代码的实现,就像我们开发web系统时按照系统设计来开发系统一样。下一次我们来说游戏单元的绘制,再上一次的总结(11中)中我们提到,使用继承的方式实现绘制。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Python进阶者 Python进阶者
2年前
手把手教你使用CanvasAPI打造一款拼图游戏
一、canvas简介1.canvas是HTML5提供的一种新标签,双标签;2.HTML5 canvas标签元素用于图形的绘制,通过脚本(通常是JavaScript)来完成;3.canvas标签只是图形容器,必须使用脚本来绘制图形;Canvas是一个矩形区域的画布,可以用JavaScript在上面绘画;二、案例目标我们今天的目标是使用HTML5
Karen110 Karen110
3年前
Python-geopandas 中国地图绘制
上一期的地图可视化推文教程中,我们详细介绍了使用Rggplot2包完美绘制中国标准地图,本期推文我们则试着使用Pythongeopandas包绘制空间地图,主要的知识点如下:geopandas绘制中国地图matplotlibadd\axes()添加南海小地图绘图文件分享geopandas读取中国地图文件g
Easter79 Easter79
3年前
Threejs绘制地图(geojson)
https://juejin.im/post/5e344733e51d453ce13d2579目前接触了一些室内地图的开发工作,二维的、三维的,数据源基本都是采用geojson格式基于geojson的地图绘制目前已经有比较成熟的框架和解决方案了。但是今天我们还是要在Threejs里来简单实现一下三维数据的展示。代码地址(htt
Stella981 Stella981
3年前
OpenCV检测轮廓极点(Python C++)
    今天分享一个OpenCV检测轮廓极点实例,原图如下,我们需要检测出地图中最大轮廓的上下左右四个极点,并进行标注显示。!(https://oscimg.oschina.net/oscnet/ae374a72c5404b00b0e976e499eedf36.png)    第一步:阈值处理分割出地图轮廓!(ht
Wesley13 Wesley13
3年前
Unity RPG游戏,场景任务的设计
0:讨论群qq群号:390313628unity4.6版本运行1场景任务的设计参考开源赛达尔传说游戏SolarusDX。每个地图存在一个控制脚本,脚本名字mapxxxxxx为地图ID。地图控制器提供标准接口,进入地图事件处理和退出地图事件处理。每次切换场景地图的时候,加载对应的脚本,脚本挂在一个Game
Stella981 Stella981
3年前
Android Camera&Matrix图像变换
Camera与MatrixAndroidUI系统中,Camera充当着相机的角色,无论是系统成像还是UI绘制。都离不开Camera。但是在Android系统中,存在两种Camera,一种是视觉成像的(拍照、摄像),另一种是图形绘制(游戏、地图、3D),实际上两种也都离不开Matrix,所以本质上可以理解为,一个负责对相机以外
Wesley13 Wesley13
3年前
View绘制系列(10)
Canvas基础变换前面学习了Canvas相关的一些绘制方法,不知道大家发现没?我们都是根据左上角(0,0)点算出来新的坐标,然后再绘制,这样明显不符合我们平常基于坐标原点绘制的习惯,那么我们能不能直接在原点绘制图形,然后通过操作Canvas来实现移动到目标位置呢?答案是肯定的,这就需要用到Canvas变换方法了。Canvas变
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这