【CSON原创】HTML5游戏框架cnGameJS开发实录(外部输入模块篇)
返回目錄
1.為什么我們需要外部輸入模塊?
在游戲中我們常常用到類似這樣的操作:鼠標(biāo)點(diǎn)擊某位置,玩家對象移動到該位置,或者按鼠標(biāo)方向鍵,玩家向不同方向移動,等等。這些操作無一不用與外部輸入設(shè)備打交道。作為游戲的設(shè)計(jì)者,我們很需要在任何時(shí)候知道鼠標(biāo)目前的位置,鍵盤的點(diǎn)擊狀況等,從而方便我們對游戲元素加以控制。因此作為一個(gè)游戲框架,外部輸入模塊也是必不可少的。
2.提供哪些功能,怎樣使用?
外部輸入模塊主要實(shí)現(xiàn)的功能就是動態(tài)記錄鼠標(biāo)相對于canvas的位置,以及記錄鍵盤上哪些鍵是按下的,哪些鍵剛剛松開,并觸發(fā)相應(yīng)的回調(diào)函數(shù)。
我們可以通過框架保存的兩個(gè)字段獲取鼠標(biāo)當(dāng)前在canvas的位置:
?
var x=cnGame.input.mouseX;
var y=cnGame.input.mouseY;
由于canvas下的游戲編程模式是通過一個(gè)游戲循環(huán)來實(shí)現(xiàn)的幀動畫(關(guān)于游戲循環(huán)請看:HTML5游戲框架cnGameJS開發(fā)實(shí)錄(游戲循環(huán)篇)),因此單純對鍵盤keyup與keydown的綁定,往往并不能達(dá)到期望效果,舉個(gè)例子,我們?nèi)绻朐阪I盤按下左鍵時(shí)使元素一直向左移動:
?
cnGame.input.onKeyDown("left",function(){
player.move(-10);
}) 我們會發(fā)現(xiàn)這種方法并不能很好的運(yùn)用在幀動畫的編程模型。由于當(dāng)我們按著鍵盤左方向鍵時(shí),其回調(diào)函數(shù)會不斷觸發(fā),因此觸發(fā)頻率并不能和你的幀動畫的頻率一致(要么太快要么太慢,取決于你的幀頻率),所以更好的選擇是每次幀更新時(shí),判斷左鍵是否按下,如果是按下游戲元素就向左移動一定位置,這樣游戲元素就成為幀動畫的一部分,隨著每次幀的更新而更新:
?
/*每次幀更新調(diào)用的函數(shù)*/
var update=function(){
cnGame.input.isPressed("left",function(){player.move(-10);})
}
3.代碼實(shí)現(xiàn)
首先看如何保持鼠標(biāo)在canvas的位置。鼠標(biāo)相對于canvas的位置,其實(shí)就是鼠標(biāo)相對于頁面的位置和canvas的位置之差。在之前的HTML5游戲框架cnGameJS開發(fā)實(shí)錄(核心函數(shù)模塊篇)里已經(jīng)介紹過,在框架的初始化函數(shù)里,我們已經(jīng)通過getCanvasPos獲取到canvas在頁面的位置,因此鼠標(biāo)相對于canvas的位置可以如此計(jì)算:
?
/**
*記錄鼠標(biāo)在canvas內(nèi)的位置
**/
var recordMouseMove=function(eve){
var pageX,pageY,x,y;
eve=cg.core.getEventObj(eve);
pageX = eve.pageX || eve.clientX + document.documentElement.scrollLeft - document.documentElement.clientLeft;
pageY = eve.pageY || eve.clientY + document.documentElement.scrollTop - document.documentElement.clientTop;
cg.input.mouseX=pageX-cg.x;
cg.input.mouseY=pageY-cg.y;
}
之后再看看鍵盤輸入的記錄如何實(shí)現(xiàn),我們需要一個(gè)數(shù)組,保存每個(gè)鍵的名值對(鍵名和鍵編碼),以及一些對象,保存每個(gè)鍵對應(yīng)的按下和松開的回調(diào)函數(shù),還有最后一個(gè)對象,保存那些需要禁止默認(rèn)行為的鍵名。(禁止鍵盤默認(rèn)行為在游戲開發(fā)中很必要,可以防止玩家在操控時(shí)游戲?qū)ο髸r(shí)觸發(fā)不必要的瀏覽器默認(rèn)行為,例如滾動條滾動等)。
首先是建立鍵名和鍵編碼的字典:
/**
*鍵盤按鍵編碼和鍵名
**/
var k=[];
k[8] = "backspace"
k[9] = "tab"
k[13] = "enter"
k[16] = "shift"
k[17] = "ctrl"
k[18] = "alt"
k[19] = "pause"
k[20] = "capslock"
k[27] = "esc"
k[32] = "space"
k[33] = "pageup"
k[34] = "pagedown"
k[35] = "end"
k[36] = "home"
k[37] = "left"
k[38] = "up"
k[39] = "right"
k[40] = "down"
k[45] = "insert"
k[46] = "delete"
k[91] = "leftwindowkey"
k[92] = "rightwindowkey"
k[93] = "selectkey"
k[106] = "multiply"
k[107] = "add"
k[109] = "subtract"
k[110] = "decimalpoint"
k[111] = "divide"
k[144] = "numlock"
k[145] = "scrollock"
k[186] = "semicolon"
k[187] = "equalsign"
k[188] = "comma"
k[189] = "dash"
k[190] = "period"
k[191] = "forwardslash"
k[192] = "graveaccent"
k[219] = "openbracket"
k[220] = "backslash"
k[221] = "closebracket"
k[222] = "singlequote"
var numpadkeys = ["numpad1","numpad2","numpad3","numpad4","numpad5","numpad6","numpad7","numpad8","numpad9"]
var fkeys = ["f1","f2","f3","f4","f5","f6","f7","f8","f9"]
var numbers = ["0","1","2","3","4","5","6","7","8","9"]
var letters = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"]
for(var i = 0; numbers[i]; i++) { k[48+i] = numbers[i] }
for(var i = 0; letters[i]; i++) { k[65+i] = letters[i] }
for(var i = 0; numpadkeys[i]; i++) { k[96+i] = numpadkeys[i] }
for(var i = 0; fkeys[i]; i++) { k[112+i] = fkeys[i] }
有點(diǎn)長,不過其實(shí)沒啥技術(shù)含量,就是方便我們以后知道某個(gè)編碼的鍵名是什么。例如我們按下左鍵,那么流程就是:獲取到左鍵的鍵盤編碼->在字典中得到鍵名->在對象中通過鍵名獲取到之前同樣通過鍵名保存的處理程序,并執(zhí)行。
為鍵盤綁定處理程序的代碼如下:
/**
*記錄鍵盤按下的鍵
**/
var recordPress=function(eve){
eve=cg.core.getEventObj(eve);
var keyName=k[eve.keyCode];
pressed_keys[keyName]=true;
if(keydown_callbacks[keyName]){
for(var i=0,len=keydown_callbacks[keyName].length;i<len;i++){
keydown_callbacks[keyName][i]();
}
}
if(keydown_callbacks["allKeys"]){
for(var i=0,len=keydown_callbacks["allKeys"].length;i<len;i++){
keydown_callbacks["allKeys"][i]();
}
}
if(preventDefault_keys[keyName]){
cg.core.preventDefault(eve);
}
}
每個(gè)鍵的處理程序可以有多個(gè),所以這里保存處理程序的對象保存的是一個(gè)數(shù)組。另外需要注意通過pressed_keys數(shù)組保存了按下的鍵(pressed_keys[keyName]=true;),就是為了方便實(shí)現(xiàn)之前說過的在幀更新中進(jìn)行一致的參數(shù)更新(可以在每次update時(shí)通過isPressed(keyName)判斷某個(gè)鍵是否按下)。 ?
最后附上該輸入模塊所有源代碼:
/**
*
*輸入記錄模塊
*
**/
cnGame.register("cnGame.input",function(cg){
this.mouseX=0;
this.mouseY=0;
/**
*記錄鼠標(biāo)在canvas內(nèi)的位置
**/
var recordMouseMove=function(eve){
var pageX,pageY,x,y;
eve=cg.core.getEventObj(eve);
pageX = eve.pageX || eve.clientX + document.documentElement.scrollLeft - document.documentElement.clientLeft;
pageY = eve.pageY || eve.clientY + document.documentElement.scrollTop - document.documentElement.clientTop;
cg.input.mouseX=pageX-cg.x;
cg.input.mouseY=pageY-cg.y;
}
cg.core.bindHandler(window,"mousemove",recordMouseMove);
/**
*被按下的鍵的集合
**/
var pressed_keys={};
/**
*要求禁止默認(rèn)行為的鍵的集合
**/
var preventDefault_keys={};
/**
*鍵盤按下觸發(fā)的處理函數(shù)
**/
var keydown_callbacks={};
/**
*鍵盤彈起觸發(fā)的處理函數(shù)
**/
var keyup_callbacks={};
/**
*鍵盤按鍵編碼和鍵名
**/
var k=[];
k[8] = "backspace"
k[9] = "tab"
k[13] = "enter"
k[16] = "shift"
k[17] = "ctrl"
k[18] = "alt"
k[19] = "pause"
k[20] = "capslock"
k[27] = "esc"
k[32] = "space"
k[33] = "pageup"
k[34] = "pagedown"
k[35] = "end"
k[36] = "home"
k[37] = "left"
k[38] = "up"
k[39] = "right"
k[40] = "down"
k[45] = "insert"
k[46] = "delete"
k[91] = "leftwindowkey"
k[92] = "rightwindowkey"
k[93] = "selectkey"
k[106] = "multiply"
k[107] = "add"
k[109] = "subtract"
k[110] = "decimalpoint"
k[111] = "divide"
k[144] = "numlock"
k[145] = "scrollock"
k[186] = "semicolon"
k[187] = "equalsign"
k[188] = "comma"
k[189] = "dash"
k[190] = "period"
k[191] = "forwardslash"
k[192] = "graveaccent"
k[219] = "openbracket"
k[220] = "backslash"
k[221] = "closebracket"
k[222] = "singlequote"
var numpadkeys = ["numpad1","numpad2","numpad3","numpad4","numpad5","numpad6","numpad7","numpad8","numpad9"]
var fkeys = ["f1","f2","f3","f4","f5","f6","f7","f8","f9"]
var numbers = ["0","1","2","3","4","5","6","7","8","9"]
var letters = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"]
for(var i = 0; numbers[i]; i++) { k[48+i] = numbers[i] }
for(var i = 0; letters[i]; i++) { k[65+i] = letters[i] }
for(var i = 0; numpadkeys[i]; i++) { k[96+i] = numpadkeys[i] }
for(var i = 0; fkeys[i]; i++) { k[112+i] = fkeys[i] }
/**
*記錄鍵盤按下的鍵
**/
var recordPress=function(eve){
eve=cg.core.getEventObj(eve);
var keyName=k[eve.keyCode];
pressed_keys[keyName]=true;
if(keydown_callbacks[keyName]){
for(var i=0,len=keydown_callbacks[keyName].length;i<len;i++){
keydown_callbacks[keyName][i]();
}
}
if(keydown_callbacks["allKeys"]){
for(var i=0,len=keydown_callbacks["allKeys"].length;i<len;i++){
keydown_callbacks["allKeys"][i]();
}
}
if(preventDefault_keys[keyName]){
cg.core.preventDefault(eve);
}
}
/**
*記錄鍵盤松開的鍵
**/
var recordUp=function(eve){
eve=cg.core.getEventObj(eve);
var keyName=k[eve.keyCode];
pressed_keys[keyName]=false;
if(keyup_callbacks[keyName]){
for(var i=0,len=keyup_callbacks[keyName].length;i<len;i++){
keyup_callbacks[keyName][i]();
}
}
if(keyup_callbacks["allKeys"]){
for(var i=0,len=keyup_callbacks["allKeys"].length;i<len;i++){
keyup_callbacks["allKeys"][i]();
}
}
if(preventDefault_keys[keyName]){
cg.core.preventDefault(eve);
}
}
cg.core.bindHandler(window,"keydown",recordPress);
cg.core.bindHandler(window,"keyup",recordUp);
/**
*判斷某個(gè)鍵是否按下
**/
this.isPressed=function(keyName){
return !!pressed_keys[keyName];
};
/**
*禁止某個(gè)鍵按下的默認(rèn)行為
**/
this.preventDefault=function(keyName){
if(cg.core.isArray(keyName)){
for(var i=0,len=keyName.length;i<len;i++){
arguments.callee.call(this,keyName[i]);
}
}
else{
preventDefault_keys[keyName]=true;
}
}
/**
*綁定鍵盤按下事件
**/
this.onKeyDown=function(keyName,handler){
keyName=keyName||"allKeys";
if(cg.core.isUndefined(keydown_callbacks[keyName])){
keydown_callbacks[keyName]=[];
}
keydown_callbacks[keyName].push(handler);
}
/**
*綁定鍵盤彈起事件
**/
this.onKeyUp=function(keyName,handler){
keyName=keyName||"allKeys";
if(cg.core.isUndefined(keyup_callbacks[keyName])){
keyup_callbacks[keyName]=[];
}
keyup_callbacks[keyName].push(handler);
}
/**
*清除鍵盤按下事件處理程序
**/
this.clearDownCallbacks=function(keyName){
if(keyName){
keydown_callbacks[keyName]=[];
}
else{
keydown_callbacks={};
}
}
/**
*清除鍵盤彈起事件處理程序
**/
this.clearUpCallbacks=function(keyName){
if(keyName){
keyup_callbacks[keyName]=[];
}
else{
keyup_callbacks={};
}
}
});
轉(zhuǎn)載于:https://www.cnblogs.com/Cson/archive/2012/02/14/2348684.html
總結(jié)
以上是生活随笔為你收集整理的【CSON原创】HTML5游戏框架cnGameJS开发实录(外部输入模块篇)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 泸州老窖多少钱一瓶啊?
- 下一篇: sql server 2008数据导入O