阅读 136

想用React写游戏第一天:构造与继承

游戏编程模式

《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();
复制代码

输出结果:

nuit->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#在性能上的优势不香嘛,原因有两点:

  1. 简单
  2. WEB游戏安装成本低

JavaScript作为一个脚本语言,相比较于其他语言(C,C++)更加容易理解,flash与计算机语言又有着本质上的不同。想入门游戏,不妨从前端/JavaScript开始,如果你觉得可以了的话,想做大游戏再转行C语言也不迟,因为很多编程的思维,都是互通的,学会了思想,什么都好办。(其实主要原因还是因为大学第一年就学会了用JS做动画,很方便不是么)

我这个人嘛,是从4399小游戏的时代过来的,即使现在打开也是满满的童年味。小时候想玩游戏了,打开4399,随便点开一个游戏,马上就能玩,不需要注册什么账号,不用繁琐的安装流程,方便的启动总是能让人想点开更多的游戏。这就是WEB端在小游戏上的天然优势。而作为游戏开发者,谁不是从小游戏开始的呢?奥利给!加油吧!

关注下面的标签,发现更多相似文章
评论