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.onload=function(){
map = new j2d.Map();//地圖
map.DrawObject=mapImg;
map.Width = mapImg.width;
map.Height=mapImg.height;
var heroImg = new Image();
heroImg.onload=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.onclick=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中測試通過)!

  本文(包括本系列其它文章)為原創作品,如有轉載請保留作者信息與出處,其中涉及到的圖片,來源於網絡,僅供學習研究目的使用,請勿用於其它用途,否則由此產生的任何后果均與本人無關!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM