html5 Game开发系列文章之 三 搭建基本游戏框架(代码封装)
在之前的二节中,我做出一个基本的游戏精灵--一条红色的飞行的小飞龙,但是在进行下一步开发前,我觉得有必要对现有的代码进行封装!在这一节中,我将封装一些基本的方法,并演示如何在JS中实现继承!
首先了解下什么叫立即调用的函数:
(function(){//你的代码})();
顾名思义,即函数定义完后会立即调用或执行自己,在这里,它相当于
function 函数名(){//你的代码;} 函数名();
封装在里面的代码,外部是无法访问的,这样能确保不会因为外面的同名变量而产生不可预料的异常!然后可以通过一个全局变量来访问相关内容!网上一些JS库都是这种方式封装的,包括jQuery!在这里我们建一个立即调用函数并将上一节用到的几个方法,封装进去,然后通过一个全局变量window.j2d来访问里面的方法!
(function(window){var j2d={//canves context对象 Context:undefined,//坐标 Point:function(x,y){if(isNaN(x)){ x=0; }if(isNaN(y)){ y=0; }return {"X":x,"Y":y}; },//每秒帧数 FPS:10,//精灵容器 Containet:new Array(),//八个方向 Directions:{ North:0, NorthEast:1, East:2, SouthEast:3, South:4, SouthWest:5, West:6, NorthWest:7 },//获取两点之间距离 GetDistance:function(x,y) {return Math.sqrt(Math.pow((x.X - y.X), 2) + Math.pow((x.Y - y.Y), 2)); }, //计算当前坐标与目标点之间的正切值以获取朝向 GetDirection:function(current,target){//...略 }, //是否到达指定坐标 RatherPoint:function(direction,p1,p2){//...略 }//};window.j2d=j2d;})(window);
封装好后,我们可以直接通用j2d来调用相关方法了!不过在这里要先改进一下计算移动步长GetMovePoint这个方法,上一节中没有细致考虑导致方法过于冗余!在这里改进调整下!
如果精灵是横向或者纵向移动,每次移动步长直接是当前移动速度即可,如果是斜向移动,则需要计算目标点的长度,并根据对应的比例获取x坐标长度与y坐标长度!
根据勾股定理:两条直角边的长度的平方之和等于斜边的平方,则得出获取两个坐标点的距离为Math.sqrt(Math.pow((point2.X - point1.X), 2) + Math.pow((point2.Y - point1.Y), 2));
因为同号两数相乘得正数,异号两数相乘得负数,所以这里得出的结果永远是正数!再根据 斜边/长(高) = 速度/x步长(y步长) (两者比例一致的,斜边上的移动步长即为当前速度)就能得出对应的x轴移动步长或者y轴移动步长了!优化后方法如下(方法名调整为GetMoveStep):
GetMoveStep:function(point1,point2,speed){var length = j2d.GetDistance(point1,point2);var p = j2d.Point(0,0);if(length==0){return p; } p.X= (point2.X - point1.X) * speed / length; p.Y= (point2.Y - point1.Y) * speed / length;return p;}
下面我将实现精灵对象的基类封装,每个精灵都有一个Draw方法,用来绘制精灵对象,一个Update方法,用来实现相关逻辑处理
ObjectEntity:function(){this.DrawObject=undefined;//绘制对象 this.ID=undefined;//编号 this.Name=undefined;//名字 this.Offset={"X":0,"Y":0};//偏移量 this.Containet=new Array();//子对象集合 this.Width=0;//宽 this.Height=0;//高 this.Direction=0;//朝向 this.DrawPoint={"X":0,"Y":0};//绘制起始坐标 this.Coordinate={"X":0,"Y":0};//当前所在坐标 this.Draw=function(){if(j2d.Context&&this.DrawObject){var point = this.Coordinate;var w = this.Width;var h = this.Height; j2d.Context.drawImage(this.DrawObject,//绘制对象 parseInt(this.DrawPoint.X),// parseInt(this.DrawPoint.Y),// parseInt(w),//裁剪尺寸 parseInt(h),//裁剪的尺寸 parseInt(point.X-this.Offset.X),// parseInt(point.Y-this.Offset.Y), parseInt(w),//绘制大小。 parseInt(h) //绘制大小。 );for(var n=0;n<this.Containet.length;n++){//执行子集合里面的Draw方法 if(this.Containet[n]&&this.Containet[n].Draw){this.Containet[n].Draw(gameTime); } } } } }
然后,我们再定义一个地图对象,继承自ObjectEntity,其实javascript中并不存在类,继承等东西,但是我们可以通过一些方法来实现继承等功能,实现继承有多种方法,这里使用call来实现(其它还有原型继承等,更多请自行搜索了解)!
Map:function(){ j2d.ObjectEntity.call(this);this.Update=function(gameTime){for(var n=0;n<this.Containet.length;n++){if(this.Containet[n]){if(this.Containet[n].Update){this.Containet[n].Update(gameTime); } } } }; }
Map里面只新增一个Update方法,遍历子集合里面的所有对象并运行其Update方法(注:随着后面更多的功能增加,会有更多的方法增减,但是目前为了方便理解与突出重点,用不到的方法暂时不放出来)
OK,地图定义完了,再定义一个Sprite类,在之前的演示中,我们的精灵只有一个动行,即飞行,但是这远远不够的,可能我还要有站立,行走,攻击,死亡,打坐,受伤,施法...等很多动作,在这里我不想把这些动作写死,我打算在实际开发中动态添加,在这里我定义一个AddAnimation方法,来用添加动作动画,再定义一个SetAnimation,来设置当前动作。但是精灵的基本图片要求不变,即每个方向为一行!其实在一般的游戏中,精灵图片一般都是单张单张分开的,但是为了减少HTTP请求,我将所有图片都拼合到一张图片里面了,加载的时候慢点,但是不需要频繁的去请求,智者见智,仁者见仁,如果不想一次加载的,可以自己更改相关方法,但是原理还是一致的!
Sprite:function(){ j2d.ObjectEntity.call(this);this.Speed=100;//速度 var action="",//当前动作 list,//动作列表 index=0,//当前动作的帧索引 timeStep=0;//距上次更新时的时间步长 this.AddAnimation=function(key,value){if(!list){ list=new Object(); } list[key]=value;return true; }; this.SetAnimation=function(key){if(action!=key){ index=0; action=key; timeStep=3600000;//这里将时间设为一个较大的数值是为了切换动作后能马上更换图片 } }; this.Update=function(gameTime){if(action&&action!=""&&list[action]){ timeStep+=gameTime;if(timeStep>=list[action].Interval){//是否达到每帧的切换时间 timeStep=0;this.DrawPoint=j2d.Point(list[action].Frames[index]*this.Width,this.Direction*this.Height);if(list[action].Callback){ list[action].Callback(index,action); }if(index>=list[action].Frames.length-1){ index=0; }else{ index++; } } } for(var n=0;n<this.Containet.length;n++){if(this.Containet[n]){if(this.Containet[n].Update){this.Containet[n].Update(gameTime); } } } }; }
实现了封装后,下面的开发中我们将大大简化相关代码,在下面,我将再次实现一个会行走的精灵,这次我不再打算用上次的小龙了,必竟这是老外们的龙,咱们中国人,当然要用中国元素,所以我决定用一个小美女,嗯,现在美女大家都喜欢,不是吗,特别对于代码老是报“找不到对象”的错误的同学们来说!
先展示一下我们要用到的精灵素材(这里只有部分,全图放在下载文件中,站立10帧,行走10帧,8个方向,计160张图片拼合而成,说实话,处理图片真是一个很麻烦的过程!原图可都是一张一张零散的图片)
同时,这里我也将加入地图元素,不再像上一节中精灵只能在无尽的白色背景中行走了!实现代码:
j2d.Context=document.getElementById("scene").getContext("2d"); //为Sprite添加一个RunTo方法,用来控制行走j2d.Sprite.prototype.RunTo=function(point,Callback){ clearInterval(this.runTimer);var speed=parseFloat(this.Speed/1000)*(1000/j2d.FPS);//point = arguments[0]; var me = this; var moveStep = j2d.GetMoveStep(this.Coordinate,point,this.Speed); me.runTimer=setInterval(function(){var x = parseInt(me.Coordinate.X+moveStep.X), y=parseInt(me.Coordinate.Y+moveStep.Y);if(x<0||x>(j2d.Context.canvas.width-me.Offset.X)||y<0||y>(j2d.Context.canvas.height - me.Offset.Y)||j2d.RatherPoint(me.Direction,me.Coordinate,point)){//碰撞检测与判断是否到达目标点 clearInterval(me.runTimer);if(Callback){ Callback(me); }; }else{ me.Coordinate.X=x; me.Coordinate.Y=y; } },1000/j2d.FPS)}; var map,hero;var mapImg = new Image();mapImg.οnlοad=function(){ map = new j2d.Map();//地图 map.DrawObject=mapImg; map.Width = mapImg.width; map.Height=mapImg.height;var heroImg = new Image(); heroImg.οnlοad=function(){ hero = new j2d.Sprite();//美女 hero.DrawObject=heroImg; hero.Width = 70; hero.Height=200; hero.Speed=5;//移动速度 hero.Offset = j2d.Point(35,160); hero.Coordinate = j2d.Point(400,350);//所处位置 hero.AddAnimation("Stand",{//添加动作-站立 Interval:10, Frames:[0,1,2,3,4,5,6,7,8,9] }); hero.AddAnimation("Walk",{//添加动作 - 行走 Interval:10, Frames:[10,11,12,13,14,15,16,17,18,19] }); hero.SetAnimation("Stand");//设置当前动作为-站立 map.Containet.push(hero); j2d.Containet.push(map); j2d.Context.canvas.οnclick=function(){//点击事件 var point = j2d.GetMousePoint();//获取点击坐标 hero.Direction=j2d.GetDirection(hero.Coordinate,point);//设置朝向 hero.SetAnimation("Walk");//设置行走动画 hero.RunTo(point,function(){//开始行走 hero.SetAnimation("Stand");//到达目标点的回调方法:此处为改变动作为站立 }); }; j2d.Run();//开始执行 }; heroImg.src="Data/Sprite/1.png";};mapImg.src = "Data/Map/1.gif";
好,我们来运行一下结果吧:
冰山上一个漂亮的美女正在孤独的行走,慢慢前行...,冰山上的美女,简称冰女,要是石头上的美女,简称...咳咳,我什么都没有说啊...
今天本节到此为止,开发已经渐入佳境了,不过,还有很多功能没有出来,比如地图没有障碍,人物没有阴影,2.5D效果处理,地图切换,怪物,技能等,不过别急,请关注本人的Html5 Game开始系列文章,让我们一起走近html5,感受html5的魅力,享受html5之神奇之旅...
作者:翅膀的初衷
QQ:4585839
总目录:点击进入
本章代码下载:点击下载
本系列效果演示:http://www.iis0.com/html5
这篇文章是旧历2011年的最后一篇文章了,过两天要回家过年了,下面的更新将在节后回来继续。如果代码与文章中存在不足之处,欢迎大家指点(本代码在IE9,火狐(中国版)9.01中测试通过)!
本文(包括本系列其它文章)为原创作品,如有转载请保留作者信息与出处,其中涉及到的图片,来源于网络,仅供学习研究目的使用,请勿用于其它用途,否则由此产生的任何后果均与本人无关!
转载于:https://www.cnblogs.com/hnvvv/archive/2012/01/18/2326025.html
html5 Game开发系列文章之 三 搭建基本游戏框架(代码封装)相关推荐
- html5 Game开发系列文章之 二 精灵(下)
昨天在家装了IE9,感觉不错,界面终于清爽了,跑HTML5也还不错!期待IE10正式版! 上一节我简单了介绍了HTML5 canvas 中的 drawImage方法,并绘制了一条扇动翅膀的小红龙,那么 ...
- 音视频开发系列(12):音频录制的代码封装
这节课分享一下如何对基于qt的音频推流的代码进行封装,首先先说明一下封装的目的,主要是为了以后如何有多种方案可以进行使用时,只需要在该封装的类上提供新的接口即可.本次封装的类的基类为XAudioRec ...
- Windows Mobile 开发系列文章收藏 - Windows Mobile 6.x
收集整理一些Windows Mobile 6.x开发相关文章, 文章及相关代码大部分搜集自网络,版权属于原作者! 智能手机 手机词汇 研发手机基本流程 WAP协议分析(1) ...
- Jerry Wang的微信公众号开发系列文章
微信程序开发系列教程(一)开发环境搭建 微信程序开发系列教程(二)使用JavaScript给微信用户发送消息 微信程序开发系列教程(二)微信订阅号+人工智能问答服务 微信程序开发系列教程(三)使用微信 ...
- [转载]C# WinForm开发系列 - 文章索引
该系列主要整理收集在使用C#开发WinForm应用文章及相关代码, 平时看到大家主要使用C#来开发Asp.Net应用,这方面的文章也特别多,而关于WinForm的文章相对少很多,而自己对WinForm ...
- Android蓝牙开发系列文章-蓝牙设备类型知多少?
在写<Android蓝牙开发系列文章-蓝牙音箱连接>时,计划细化出一篇讲解蓝牙设备类型的文章,现在它来了~ 阅读其他内容,可以点击<Android蓝牙开发系列文章-策划篇>,或 ...
- ZYNQ开发系列——ZYNQ系统的搭建
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 ZYNQ开发系列--ZYNQ系统的搭建 DDR设置 FLASH设置 UART设置 网口设置 当我们有一个要用ZYNQ做的项目时,首先当 ...
- AARCH64 开发系列1: AARCH64 环境搭建
首发极术社区 作者:Zhiyuan zhu 如对Arm相关技术感兴趣,欢迎私信aijishu20加入技术微信群. 概述 近年来 Arm 服务器的发展势头很猛,但大部分人的个人电脑还是 x86 环境,开 ...
- STM32 cubemx 开发系列文章(一)认识cubemx
STM32 cubemx 开发系列文章 新建一个cubemx工程 1.下载stm32 cubemx软件 2.安装软件 3.配置软件 4.开始第一个Hello World工程配置 写在最后 新建一个cu ...
最新文章
- Windows Phone开发(41):漫谈关键帧动画之下篇
- constraint的一些用法总结
- 互联网1分钟 | 1015
- java 引用队列_Java中管理资源的引用队列相关原理解析
- 优秀!史学博士在Science杂志发表一篇学术评论
- SpringCloud 从菜鸟到大牛之五 统一配置中心 Spring Cloud Config
- (组合数学笔记)格点路径问题分析求解
- 大数据可视化的方法和价值
- 网络工程设计教程--系统集成方法
- 现代操作系统 第三章 内存管理 习题答案
- 阮一峰ES6入门读书笔记(九):Set 和 Map
- 全自动共享软件破解器4.8
- 关于计算机方面英语ppt模板,经典ppt模板--计算机软件.ppt
- scum服务器里找不到车,人渣SCUM车辆机制介绍 人渣SCUM车辆为什么消失
- 2毫秒c51汇编语言延时函数,单片机精确毫秒延时函数
- web前端高级必备面试资料
- 关于SQLの大题练习
- 【Navicat】连接Oracle报错 ORA-12505
- iphone的shsh备份实用方法
- oracle 数据类型的变更无效 clob,ORA-22858:数据类型的变更无效varchar2类型转换为clob类型...
热门文章
- web攻击有哪些方式?小白需要知道的几种攻击方式
- 免费开源漏洞扫描工具+商业级
- ZZULIOJ:1051: 平方根的和
- c++第八周【任务2】实现Time类中的运算符重载
- 数据库的应用(一):建库、建表、录数据最基础操作
- mysql 5.7 tokudb_percona 5.7 + tokudb
- oledb mysql_oledb方式连接mysql5 ado连接MySQL[未验证]
- idea远程连接工具使用
- 华硕笔记本,wifi图标不见,连不上网的解决方法
- java iterable 使用_Iterable(迭代器)的用法