游戏编程模式
《game programming patterns》 这是一本讲设计模式在游戏中的应用的书,我觉得作者讲得很好,有兴趣的小伙伴可以去看看原著。如果懂得编码的快乐的话,你一定会乐在其中的。这也是原著里我很喜欢的一句话: if you want to make something fun, have fun making it. (如果你想做出让人享受的东西,那就享受做它的过程)
我接下来的文章也会围绕这本书展开,使用React+JavaScript实现一些这本书上的简单例子。 好了,马上开始吧。
第一天:构造与继承
1.构造
在学习游戏设计模式之前,有必要先了解一下继承
MilK:你能不能用键盘控制DIV移动。
丁丁:简单。
一天后...
丁丁:做完了。
MilK:效果不错,你能不能多加几个这样的DIV。
丁丁:简单,复制出来就好了。
MilK:复制?那你也太捞了。试试用构造的方式写吧。
丁丁:好吧,我想想...
单元(unit)是游戏中最简单也是最重要的一部分,想要高效的生成各种各样的元素,肯定离不单元的开构造与继承。
来一个简单的构造函数:
function CreateUnit (){
this.width = 100;//宽度
this.height = 100;//高度
this.left = 100;//X坐标
this.top = 100;//Y坐标
this.v = 100;//移动速度
this.move = function (left,top) {//移动到某个坐标
this.left = left;
this.top = top;
}
}
然后把这个单元new出来
unit = new CreateUnit();
输出结果:
unit->CreateUnit {
width: 100,
height: 100,
left: 100,
top: 100,
v: 100,
move: ƒ (left, top),
__proto__: Object
}
然后把属性赋予给DIV:(这里是REACT的写法,如果不用React的话可以用createElement或getElementById,我个人觉得React更方便)
<div
style = {{
border:"1px black solid",
width:this.nuit.width,
height:this.nuit.height,
left:this.nuit.left-this.nuit.width/2,
top:this.nuit.top-this.nuit.height/2,
position:"absolute",
}}
/>
这样就能看到一个长100px宽100px的框框浮动在页面上了
2.继承
我们对这个构造函数稍加修改,让它可以附上初值:
function CreateUnit (unit){
this.width = unit.width||100;//宽度
this.height = unit.height||100;//高度
this.left = unit.left||100;//X坐标
this.top = unit.top||100;//Y坐标
this.v = unit.v||100;//移动速度
this.move = function (left,top) {//移动函数
this.left = left;
this.top = top;
}
}
不过这个最简单的肯定不够,我们不妨再定义一个Boy的构造函数,继承这个unit:
function Boy (boy){
this.name = boy.name||"boy";//名字
}
Boy.prototype = new CreateUnit({});
就是这么简单,一个最基本的继承关系就实现了,Boy会具有自己的名字,和继承来的unit属性(有关于prototype的基础可以去百度找到更详细的教学)
现在再构造出一个Boy看看:
boy = new Boy({
name: "小明"
});
输出结果:
boy->Boy{
name: "小明",
__proto__: CreateUnit {
width: 100,
height: 100,
left: 100,
top: 100,
v: 100,
move: ƒ (left, top),
__proto__: Object
}
}
这里的__proto__指的是父类继承来的属性,调用父类的值不需要输入__proto__,自己没有的属性js就会自动去父类中找(知识点:原型模式,划重点,要考):
console.log(unit.width);//100,自己的属性
console.log(boy.name);//小明,自己的属性
console.log(boy.width);//100,继承来的属性
继承实现了,是时候批开始量生产了:
boyList=[
new Boy({name:"小王"}),
new Boy({name:"小北"}),
new Boy({name:"小清"}),
new Boy({name:"小狗"})
];
render 函数中显示:
{
this.boyList.map((item,i)=>{
return <div
key = {i}
style = {{
border:"1px black solid",
width:item.width,
height:item.height,
left:item.left-item.width/2,
top:item.top-item.height/2,
position:"absolute",
textAlign:"center",
lineHeight:item.height+"px"
}}
>
{item.name}
</div>
})
}
效果:
可以看到4个Boy的名字重叠出现在了页面上,批量生产大功告成~
3.角色移动
接下来就需要控制我们的角色移动了:
this.boy.move(200,100);//把小明移动到200,100的位置
看看效果:
还可以吧~接下来结合键盘的操作:
boyList = [new Boy({name:"小王"}),new Boy({name:"小北"}),new Boy({name:"小清"}),new Boy({name:"小狗"}),];
boy = new Boy({name:"小明"});
focus = this.boy;//焦点,键盘只控制焦点上的角色,初始值设为小明
/**
* REACT生命周期函数,组件渲染后调用
*/
componentDidMount(){
//添加键盘监听
document.addEventListener("keydown", this.onKeyDown);
}
/**
* REACT生命周期函数,组件销毁前调用
*/
componentWillUnmount(){
//键盘监听结束
document.removeEventListener("keydown", this.onKeyDown);
}
/**
* 键盘监听事件
* @param e
*/
onKeyDown = (e) => {
console.log(e.keyCode);
switch(e.keyCode) {
case 39://向右移动
this.focus.move(this.focus.left+this.focus.v,this.focus.top);//焦点向右移动
this.setState({});//重新渲染页面
break;
case 37://向左移动
this.focus.move(this.focus.left-this.focus.v,this.focus.top);
this.setState({});
break;
case 38://向上移动
this.focus.move(this.focus.left,this.focus.top-this.focus.v);
this.setState({});
break;
case 40://向下移动
this.focus.move(this.focus.left,this.focus.top+this.focus.v);
this.setState({});
break;
default:
break
}
};
/**
* 设置焦点
*/
setFocus = (util) => {
this.focus=util;
};
/**
* REACT生命周期主函数,用于渲染页面
* @returns {*}
*/
render() {
return (
<div className = {"game1"}>
<div
onClick = {onClick}
style = {{
border:"1px black solid",
width:this.boy.width,
height:this.boy.height,
left:this.boy.left-this.boy.width/2,
top:this.boy.top-this.boy.height/2,
position:"absolute",
textAlign:"center",
lineHeight:this.boy.height+"px"
}}
>
{this.boy.name}
</div>
{
this.boyList.map((item,i) => {//循环输出boyList
return <div
key = {i}
onClick = {onClick}
style = {{
border:"1px black solid",
width:item.width,
height:item.height,
left:item.left-item.width/2,
top:item.top-item.height/2,
position:"absolute",
textAlign:"center",
lineHeight:item.height+"px"
}}
>
{item.name}
</div>
})
}
</div>
);
}
这里解释一下焦点focus,我们肯定得让键盘分开控制不同的角色,所以,设置一个焦点给键盘控制,焦点就是某个角色的引用。我们再给每一个div设置一个onClick函数,把焦点设置为自己,就可以在点击这个div之后控制这个角色了。
看看效果:(虽然丑了点,但是意会就好啦,这就是一个多角色游戏的最基本实现)
4.优化
为了让代码更加好看一点,把角色做成一个组件吧,修改这个组件的样式,就能按照你的喜好建立角色啦!
BoyUnit组件:
class BoyUnit extends Component{
render() {
const {onClick,unit} = this.props;
return <div
onClick = {onClick}
style = {{
border:"1px black solid",
width:unit.width,
height:unit.height,
left:unit.left-unit.width/2,
top:unit.top-unit.height/2,
position:"absolute",
textAlign:"center",
lineHeight:unit.height+"px"
}}
>
{unit.name}
{/**这里还可以添加图片,甚至修改角色的形状**/}
{/*<img/>*/}
</div>;
}
}
Render函数:(看起来是不是清爽很多了)
render() {
return (
<div className = {"game1"}>
{
this.boyList.map((item,i)=>{
return <BoyUnit
key = {i}
onClick = {()=>{
this.setFocus(item);
}}
unit = {item}
/>
})
}
<BoyUnit
onClick = {()=>{
this.setFocus(this.boy);
}}
unit = {this.boy}
/>
</div>
);
}
代码优化完之后总是能让人心情舒畅,不是么~~
好了,学习React小游戏开发第一天,复习了一下JS基础,之后会学习更复杂的应用场景,写更有趣的demo,所有的demo都能在git上找到嗷。
Github源码
为什么用JavaScript写游戏
哈哈相信大家一定都有一定的困惑,为什么要用JS写游戏呢,C、C#在性能上的优势不香嘛,原因有两点:
- 简单
- WEB游戏安装成本低
JavaScript作为一个脚本语言,相比较于其他语言(C,C++)更加容易理解,flash与计算机语言又有着本质上的不同。想入门游戏,不妨从前端/JavaScript开始,如果你觉得可以了的话,想做大游戏再转行C语言也不迟,因为很多编程的思维,都是互通的,学会了思想,什么都好办。(其实主要原因还是因为大学第一年就学会了用JS做动画,很方便不是么)
我这个人嘛,是从4399小游戏的时代过来的,即使现在打开也是满满的童年味。小时候想玩游戏了,打开4399,随便点开一个游戏,马上就能玩,不需要注册什么账号,不用繁琐的安装流程,方便的启动总是能让人想点开更多的游戏。这就是WEB端在小游戏上的天然优势。而作为游戏开发者,谁不是从小游戏开始的呢?奥利给!加油吧!