React, TypeScript 寫游戲探索

如何用 React, TypeScript 寫游戲?

image

1. React的優(yōu)勢(shì)

  • 數(shù)據(jù)驅(qū)動(dòng), 根據(jù)state或者props的變化 => 視圖的變化, 以前的方式往往是直接操作 DOM 實(shí)現(xiàn), 觸發(fā)某事件使得元素移動(dòng)代碼類似如:
=>
    this.moveRight = () => {
        this.left += 8;
        this.draw();
    }

    this.draw = () => {
        if(this.ele === null){
            this.ele = document.createElement('img');
            this.ele.src = this.url;
            this.ele.style.width = this.width + 'px';
            this.ele.style.height = this.height + 'px';
            this.ele.style.position = 'absolute';
            app.appendChild(this.ele);
        }
        this.ele.style.left = this.left + 'px';
        this.ele.style.top = this.top + 'px';
    };

現(xiàn)在就友好很多

=>
    this.moveRight = () => {
        this.setState( preState => (
            {
                left: preState.left + 8
            }
        ));
    }

    <ContraBG
        left={left}
        top={top}
        status={status}
        toward={toward}>
    </ContraBG>
  • 結(jié)構(gòu)更清晰, 逐個(gè)書(shū)寫需要渲染的組件, 能讓人一目了然的知道游戲運(yùn)行中加載的組件, 老的方式代碼風(fēng)格去渲染一個(gè)元素如
=>
    const plane = new ourplane();
    plane.draw();

如果渲染的多了結(jié)構(gòu)復(fù)雜了,閱讀就會(huì)十分困難。現(xiàn)在的代碼風(fēng)格就能夠一目了然的看到所有運(yùn)行的組件

=>
    @observer
    class InGame extends React.PureComponent<InGameProps, {}> {
        render() {
            const { store } = this.props;
    
            return (
                <InGameBG   // 包裹組件負(fù)責(zé)渲染背景變化相關(guān)
                        store={store}>
                        <Contra // 玩家控制的角色組件
                            store={store}/>
                        <BulletsMap // 負(fù)責(zé)渲染子彈
                            store={store}/>
                        <EnemiesMap // 負(fù)責(zé)渲染敵方角色
                            store={store}/>
                </InGameBG>
            );
        }
    }

2. React的劣勢(shì)

  • 靈活性
    前者類與類之間繼承會(huì)靈活很多, 如
    飛機(jī)繼承至飛行物 => 飛行物繼承至動(dòng)態(tài)物 => 動(dòng)態(tài)物繼承至某一特性物體

其中子彈也可以繼承至飛行物使得飛行物等可以衍生更多子類垒在。React中各組件只能繼承至React.Component,可采用HOC高階組件思想去渲染一系列具有相似性質(zhì)的組件栗精。如超級(jí)瑪麗游戲中有許多的墻,它們具有相似的渲染邏輯,以及一些都會(huì)需要用到的方法, 可以通過(guò)寫一個(gè)靜態(tài)方塊的高階組件去生成, 能夠更高效的管理代碼撒犀。

=>
    function WithStaticSquare<TOwnProps>(options: StaticSquareOption):ComponentDecorator<TOwnProps> {
        return Component =>
            class HocSquare extends React.Component<TOwnProps, HocSquareState> {
                // xxx
                render() {
                    const { styles, className } = this.state;
                    const passThroughProps: any = this.props;
                    const classNames = className ? `staticHocWrap ${className}` : "staticHocWrap";
                    const staticProps: WrappedStaticSquareUtils = {
                        changeBackground: this.changeBackground,
                        toTopAnimate: this.toTopAnimate
                    };  // 提供一些可能會(huì)用到的改變背景圖的方法以及被撞時(shí)調(diào)用向上動(dòng)畫的方法
    
                    return (
                        <div
                            className={classNames}
                            style={styles}>
                            <Component
                                hoc={staticProps}
                                {...passThroughProps}/>
                        </div>
                    );
                }
            }
    }

3. 性能問(wèn)題

  • 避免卡頓 前者直接操作某個(gè)DOM渲染不會(huì)有太多卡頓現(xiàn)象發(fā)生
    React使用Mobx, Redux等進(jìn)行整個(gè)游戲數(shù)據(jù)控制時(shí), 如果不對(duì)渲染進(jìn)行優(yōu)化, 當(dāng)store某個(gè)屬性值變化導(dǎo)致所有接入props的組件都重新渲染一次代價(jià)是巨大的!
  1. 采用PureComponent某些組件需要這樣寫
=>
    class Square extends React.PureComponent<SquareProps, {}> {
        // xxx
    }

其中就需要了解PureComponent。React.PureComponent是2016.06.29 React 15.3中發(fā)布副渴。

image

PureComponent改變了生命周期方法shouldComponentUpdate解愤,并且它會(huì)自動(dòng)檢查組件是否需要重新渲染骤素。這時(shí)家淤,只有PureComponent檢測(cè)到state或者props發(fā)生變化時(shí)异剥,PureComponent才會(huì)調(diào)用render方法,但是這種檢查只是淺計(jì)較這就意味著嵌套對(duì)象和數(shù)組是不會(huì)被比較的更多信息

  1. 多采用組件去渲染, 對(duì)比兩種方法
=>
    // 方法1.
    <InGameBG   // 包裹組件負(fù)責(zé)渲染背景變化相關(guān)
            store={store}>
            <Contra // 玩家控制的角色組件
                store={store}/>
            <BulletsMap // 負(fù)責(zé)渲染子彈
                store={store}/>
            <EnemiesMap // 負(fù)責(zé)渲染敵方角色
                store={store}/>
    </InGameBG>
    //方法2.
    <InGameBG
        store={store}>
            <Contra
                store={store}/>
            <div>
                {
                    bulletMap.map((bullet, index) => {
                    if ( bullet ) {
                        return (
                            <Bullet
                                key={`Bullet-${index}`}
                                {...bullet}
                                index={index}
                                store={store}/>
                        );
                    }
                    return null;
                })
                }
            </div>
            <EnemiesMap
                store={store}/>
    </InGameBG>

這兩種方法的區(qū)別就是在于渲染子彈是否通過(guò)組件渲染還是在父組件中直接渲染, 其中方法2的性能會(huì)有很大的問(wèn)題, 當(dāng)某個(gè)子彈變化時(shí)使得最大的容器重新渲染, 其中所有子組件也會(huì)去判斷是否需要重新渲染瑟由,使得界面會(huì)出現(xiàn)卡頓絮重。而方法1則只會(huì)在發(fā)生數(shù)據(jù)變化的子彈去渲染。

4. 需要注意的點(diǎn)

  • 及時(shí)移除監(jiān)聽(tīng), 在組件卸載時(shí)需要移除該組件的事件監(jiān)聽(tīng), 時(shí)間函數(shù)等歹苦。如游戲開(kāi)始組件
=>
    class GameStart extends React.Component<GameStartProps, {}> {
        constructor(props) {
            super(props);
    
            this.onkeydownHandle = this.onkeydownHandle.bind(this);
        }
        componentDidMount() {
            this.onkeydown();
        }
        componentWillUnmount() {
            this.destroy();
        }
        destroy(): void {
            console.log("游戲開(kāi)始! GameStart Component destroy ....");
            window.removeEventListener("keydown", this.onkeydownHandle);
        }
        onkeydownHandle(e: KeyboardEvent): void {
            const keyCode: KeyCodeType = e.keyCode;
            const {  store } = this.props;
            const { updateGameStatus } = store;
            switch ( keyCode ) {
                case 72:
                    updateGameStatus(1);
                    break;
            }
        }
        onkeydown(): void {
            window.addEventListener("keydown", this.onkeydownHandle);
        }
        render() {
            return (
                <div className="gameStartWrap">
                </div>
            );
        }
    }

5. 最近寫的 超級(jí)魂斗羅 效果與GitHub

超級(jí)魂斗羅
超級(jí)魂斗羅

https://github.com/xiaoxiaojx/SuperContra

Thank You

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末青伤,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子殴瘦,更是在濱河造成了極大的恐慌狠角,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蚪腋,死亡現(xiàn)場(chǎng)離奇詭異丰歌,居然都是意外死亡姨蟋,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門立帖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)眼溶,“玉大人,你說(shuō)我怎么就攤上這事晓勇√梅桑” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵绑咱,是天一觀的道長(zhǎng)绰筛。 經(jīng)常有香客問(wèn)我,道長(zhǎng)描融,這世上最難降的妖魔是什么铝噩? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮稼稿,結(jié)果婚禮上薄榛,老公的妹妹穿的比我還像新娘。我一直安慰自己让歼,他們只是感情好敞恋,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著谋右,像睡著了一般硬猫。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上改执,一...
    開(kāi)封第一講書(shū)人閱讀 51,365評(píng)論 1 302
  • 那天啸蜜,我揣著相機(jī)與錄音,去河邊找鬼辈挂。 笑死衬横,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的终蒂。 我是一名探鬼主播蜂林,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼拇泣!你這毒婦竟也來(lái)了噪叙?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤霉翔,失蹤者是張志新(化名)和其女友劉穎睁蕾,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡子眶,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年瀑凝,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片臭杰。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡猜丹,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出硅卢,到底是詐尸還是另有隱情射窒,我是刑警寧澤,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布将塑,位于F島的核電站脉顿,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏点寥。R本人自食惡果不足惜艾疟,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望敢辩。 院中可真熱鬧蔽莱,春花似錦、人聲如沸戚长。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)同廉。三九已至仪糖,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間迫肖,已是汗流浹背锅劝。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蟆湖,地道東北人故爵。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像隅津,于是被迫代替她去往敵國(guó)和親诬垂。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 原教程內(nèi)容詳見(jiàn)精益 React 學(xué)習(xí)指南饥瓷,這只是我在學(xué)習(xí)過(guò)程中的一些閱讀筆記剥纷,個(gè)人覺(jué)得該教程講解深入淺出痹籍,比目前大...
    leonaxiong閱讀 2,834評(píng)論 1 18
  • 深入JSX date:20170412筆記原文其實(shí)JSX是React.createElement(componen...
    gaoer1938閱讀 8,063評(píng)論 2 35
  • It's a common pattern in React to wrap a component in an ...
    jplyue閱讀 3,265評(píng)論 0 2
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,111評(píng)論 25 707
  • 有沒(méi)有那么一本書(shū)讓你惦念不忘呢铆?有沒(méi)有那么一個(gè)角色讓你倍感神奇?有沒(méi)有那個(gè)故事讓你大呼過(guò)癮蹲缠?有沒(méi)有那么一個(gè)人物成為一...
    玩英語(yǔ)閱讀 413評(píng)論 0 4