MJHD

エモさ駆動開発

HSPプログラミングコンテスト2014に応募した

f:id:wait0000:20150823192120p:plain f:id:wait0000:20150823192128p:plain 激ムズ横スクロールアクションゲーム BE GODHSPプログラミングコンテスト2014に応募した。 HSPTVを持っている人は是非プレイしてみて欲しい。

以下にBE GODのソースコードを記載する。 こんな事件もあるが、コピペして応募、などは絶対にしないで欲しい。HSPのためにも。

ソースコード内では、6000byteに抑えるため、色々と工夫をしている。そのため読みづらいコードとなっている。

be_god.hsp

#include "hsptv.as"

#enum OBJ_STATE = 0
#enum OBJ_X
#enum OBJ_Y
#enum OBJ_UPDATED

// 物体の状態
// 下に行くほど重くなる
// WATERとLAVAは液体として振る舞う
#enum STATE_STEAM = 0
#enum STATE_AIR
#enum STATE_WATER
#enum STATE_ME
#enum STATE_LAVA
#enum STATE_ROCK

// ゲームの状態
#enum STAGE_NOP = 0
#enum STAGE_TITLE
#enum STAGE_INIT
#enum STAGE_PLAYING
#enum STAGE_FINISH

#const SCREEN_W 640
#const SCREEN_H 480

// マスのサイズ
#const MASS_W 10
#const MASS_H 10

#const MASS_H_NUM SCREEN_W/MASS_W
#const MASS_V_NUM SCREEN_H/MASS_H

// 容量削減のため
#const MASS_H_NUM_DIV_2 MASS_H_NUM/2
#const MASS_V_NUM_DIV_2 MASS_V_NUM/2
#const MASS_H_NUM_DIV_4 MASS_H_NUM/4
#const MASS_V_NUM_DIV_4 MASS_V_NUM/4
#const MASS_H_NUM_PLUS_2 MASS_H_NUM+2
#const MASS_V_NUM_PLUS_2 MASS_V_NUM+2
#const MASS_H_NUM_PLUS_1 MASS_H_NUM+1
#const MASS_V_NUM_PLUS_1 MASS_V_NUM+1
#const MASS_H_NUM_MINUS_1 MASS_H_NUM-1
#const MASS_V_NUM_MINUS_1 MASS_V_NUM-1
#const MASS_H_NUM_DIV_2_MINUS_1 MASS_H_NUM/2-1
#const MASS_V_NUM_DIV_2_MINUS_1 MASS_V_NUM/2-1
#const MASS_H_NUM_DIV_2_PLUS_1 MASS_H_NUM/2+1
#const MASS_V_NUM_DIV_2_PLUS_1 MASS_V_NUM/2+1

// 何フレームごとに画面をスクロールするか
#const SCROLL_INTERVAL 10

#const PI 3.1415926535

// 容量削減のため、ゲーム中で使う文字列を変数へ格納しておく
Font_name = "Terminal"
Score_cap = "SCORE:"
Ready_cap = "READY...?"
Go_cap    = "GO!!"
Tryagain_cap = "PUSH [SPACE] TO RETURN!!"
Title_cap = "BE GOD"
Push_cap = "PUSH [SPACE] TO START!!"
Highscore_cap = "HIGHSCORE:"
Back_cap = "[ESC] Back"

// 物体の状態を格納
// 要素数が+2, +2となっているのは、画面外にSTATE_ROCKを生成するため
dim Objs, MASS_H_NUM_PLUS_2, MASS_V_NUM_PLUS_2, 4

// ゲームの状態
// STAGE_TITLE : タイトル画面
// STAGE_INIT  : Ready...Go!!の画面
// STAGE_PLAYING:実際にゲームをする画面
// STATE_FINISH :GAME OVER画面
Stage = STAGE_TITLE

Highscore = 0

// 初期化
*init
me_x = 1000 // 自機の位置
me_y = 1000 // 初めは画面外へ
Stage_frame = 0  // フレームカウント
Dead_reason = "" // 死因

// 基本的にSTATE_AIRで埋めて、画面端はSTATE_ROCKで埋める
repeat MASS_H_NUM_PLUS_2
    x = cnt
    repeat MASS_V_NUM_PLUS_2
        state = STATE_AIR
        if (x == 0 || x == MASS_H_NUM_PLUS_1 || cnt == 0 || cnt == MASS_V_NUM_PLUS_1) {
            state = STATE_ROCK
        }
        Objs(x, cnt, OBJ_STATE) = state
    loop
loop

*main

    redraw 1
    await 10
    redraw 0
    
    stick keys, 256

    // GAME OVER画面でなければフレームを加算
    if (Stage != STAGE_FINISH) { Stage_frame++ } else { goto *scene }
    
    // 物体の移動など
    repeat MASS_H_NUM, 1
        x = cnt
    
        repeat MASS_V_NUM, 1
            y = cnt
    
            // 既に更新された物体ならcontinue
            if (Objs(x, y, OBJ_UPDATED)) { continue }
    
            // 容量削減のため
            // 変数一つで4byte、演算子一つで4byte、数値一つで4byteのため、
            // あらかじめ計算して変数に入れておくと、4byteのみで済む
            x_m_1 = x-1
            x_p_1 = x+1
            y_m_1 = y-1
            y_p_1 = y+1
    
            // 近傍のState
            // 容量削減のため
            // このぐらいの次元の配列は、アクセスするだけで16byteぐらい食う
            state    = Objs(x, y, OBJ_STATE)
            rd_state = Objs(x_p_1, y_p_1, OBJ_STATE)
            ld_state = Objs(x_m_1, y_p_1, OBJ_STATE)
            d_state  = Objs(x, y_p_1, OBJ_STATE)
            r_state  = Objs(x_p_1, y, OBJ_STATE)
            rt_state = Objs(x_p_1, y_m_1, OBJ_STATE)
    
            // 基準点の近傍を調査
            steam_cnt = 0
            repeat 9
                tmp_x = x+(cnt\3)-1
                tmp_y = y+(cnt/3)-1
                tmp_state = Objs(tmp_x, tmp_y, OBJ_STATE)
                if (Objs(tmp_x, tmp_y, OBJ_STATE) == STATE_STEAM) {
                    steam_cnt++
                }
    
                // 溶岩から岩へ
                if (tmp_state==STATE_WATER && state==STATE_LAVA) {
                    Objs(x, y, OBJ_STATE) = STATE_ROCK
                    Objs(tmp_x, tmp_y, OBJ_STATE) = STATE_STEAM
                }
                // 自機と溶岩
                if (tmp_state == STATE_LAVA && state == STATE_ME) {
                    Stage = STAGE_FINISH
                    Dead_reason = "BURNED!!"
                }
            loop
    
            // 溺れ判定
            if ((Objs(x_m_1, y_m_1, OBJ_STATE) & Objs(x, y_m_1, OBJ_STATE) & rt_state) == STATE_WATER) {
                if (state == STATE_ME) {
                    Stage = STAGE_FINISH
                    Dead_reason = "DROWNED!!"
                }
            }
    
            // 取り残され判定
            if (state == STATE_ME && x==1) {
                Stage = STAGE_FINISH
                Dead_reason = "LEFT!!"
            }
    
            // 蒸気から水へ
            // 周りに4個以上の水蒸気があれば
            if (steam_cnt > 4) {
                repeat 9
                    tmp_x = x+(cnt\3)-1
                    tmp_y = y+(cnt/3)-1
                    if (Objs(tmp_x, tmp_y, OBJ_STATE) == STATE_STEAM) {
                        Objs(tmp_x, tmp_y, OBJ_STATE) = STATE_AIR
                    }
                loop
                Objs(x, y, OBJ_STATE) = STATE_WATER
            }
    
            // 自機の移動
            if (state==STATE_ME && Stage_frame\SCROLL_INTERVAL==0 && Stage==STAGE_PLAYING) {
                if (r_state != STATE_ROCK) { // 右隣に岩が無ければ
                    Objs(x, y, OBJ_STATE) = r_state
                    Objs(x_p_1, y, OBJ_STATE) = STATE_ME
                    Objs(x, y, OBJ_UPDATED) = 1
                    Objs(x_p_1, y, OBJ_UPDATED) = 1
                } else : if (rd_state != STATE_ROCK) { // 右下
                    Objs(x, y, OBJ_STATE) = rd_state
                    Objs(x_p_1, y_p_1, OBJ_STATE) = STATE_ME
                    Objs(x, y, OBJ_UPDATED) = 1
                    Objs(x_p_1, y_p_1, OBJ_UPDATED) = 1
                } else : if (rt_state != STATE_ROCK) { // 左下
                    Objs(x, y, OBJ_STATE) = rt_state
                    Objs(x_p_1, y_m_1, OBJ_STATE) = STATE_ME
                    Objs(x, y, OBJ_UPDATED) = 1
                    Objs(x_p_1, y_m_1, OBJ_UPDATED) = 1
                }
            }
    
            // 物体の移動
            // □□□
            // □ □
            // □■□
            // 対象のマスが自分より軽ければ、交換する
            if (state > d_state) {
                if (Objs(x, y_p_1, OBJ_UPDATED)) { continue }
    
                Objs(x, y, OBJ_STATE)     = d_state
                Objs(x, y_p_1, OBJ_STATE)   = state
                Objs(x, y, OBJ_UPDATED)   = 1
                Objs(x, y_p_1, OBJ_UPDATED) = 1
    
            } else {
                
                if (state == STATE_ME) { continue }
                
                if (rnd(2)) {
                    // □□□
                    // □ □
                    // ■□□
                    if (state > ld_state) {
                        if (Objs(x_m_1, y_p_1, OBJ_UPDATED)) { continue }
    
                        Objs(x, y, OBJ_STATE)       = ld_state
                        Objs(x_m_1, y_p_1, OBJ_STATE)   = state
                        Objs(x, y, OBJ_UPDATED)     = 1
                        Objs(x_m_1, y_p_1, OBJ_UPDATED) = 1
                    }
                } else {
    
                    // □□□
                    // □ □
                    // □□■
                    if (state > rd_state) {
                        if (Objs(x_p_1, y_p_1, OBJ_UPDATED)) { continue }
    
                        Objs(x, y, OBJ_STATE)   = rd_state
                        Objs(x_p_1, y_p_1, OBJ_STATE)   = state
                        Objs(x, y, OBJ_UPDATED)   = 1
                        Objs(x_p_1, y_p_1, OBJ_UPDATED) = 1
                    } else {
    
                        if (state==STATE_WATER || state==STATE_LAVA) {
                            if (state > r_state) {
                                // □□□
                                // □ ■
                                // □□□
                                if (Objs(x_p_1, y, OBJ_UPDATED)) { continue }
        
                                Objs(x, y, OBJ_STATE)     = r_state
                                Objs(x_p_1, y, OBJ_STATE)   = state
                                Objs(x, y, OBJ_UPDATED)   = 1
                                Objs(x_p_1, y, OBJ_UPDATED) = 1
                            } else {
                                // □□□
                                // ■ □
                                // □□□
                                if (Objs(x_m_1, y, OBJ_UPDATED)) { continue }
        
                                Objs(x, y, OBJ_STATE)     = Objs(x_m_1, y, OBJ_STATE)
                                Objs(x_m_1, y, OBJ_STATE)   = state
                                Objs(x, y, OBJ_UPDATED)   = 1
                                Objs(x_m_1, y, OBJ_UPDATED) = 1
                            }
                        }
                    }
                }
            }
            
            
        loop
    loop

    // 描画
    repeat MASS_H_NUM, 1
        x = cnt
        repeat MASS_V_NUM, 1
            Objs(x, cnt, OBJ_UPDATED) = 0
    
            state = Objs(x, cnt, OBJ_STATE)
            if (state == STATE_STEAM) {
                color 255, 255, 255
            }
            if (state == STATE_AIR) {
                color 200, 200, 255
            } 
            if (state == STATE_WATER) {
                color 50, 50, 255
            } 
            if (state == STATE_LAVA) {
                color 255, 0, 0
            } 
            if (state == STATE_ME) {
                // 自機の場合は、circle命令のために座標を保存しておく
                me_x = x
                me_y = cnt
            } 
            if (state == STATE_ROCK) {
                color 91, 66, 61
            }
    
            boxf (x-1)*MASS_W, (cnt-1)*MASS_H, x*MASS_W, cnt*MASS_H
        loop
    loop
    
    // 自機の表示
    color 10, 255, 10
    circle (me_x-1)*MASS_W-10, (me_y-1)*MASS_H-10, me_x*MASS_W+10, me_y*MASS_h+10, 1

// シーン別の処理
*scene
    
    // タイトル画面
    if (Stage == STAGE_TITLE) {
        if (keys & 16) { // ゲーム開始
            Stage = STAGE_INIT
    
            goto *init
        }
        if (keys & 128) { // ゲーム終了
            end
        }
        
        font Font_name, 150
        pos 155, 55 : color 0, 0, 0
        mes Title_cap // 影の描画
        pos 150, 50 : color 255, 183, 76
        mes Title_cap // 文字本体の描画
        
        font Font_name, 40
        pos 43, 303 : color 0, 0, 0
        mes Push_cap
        pos 40, 300 : color 40, 175, 12
        mes Push_cap
    
        font Font_name, 35
        pos 203, 193 : color 0, 0, 0
        mes Highscore_cap+Highscore
        pos 200, 190 : color 40, 175, 12
        mes Highscore_cap+Highscore
    
        font Font_name, 32
        pos 402, 402 : color 0, 0, 0
        mes Back_cap
        pos 400, 400 : color 40, 175, 12
        mes Back_cap
    
        // 背景のDEMO
        state = rnd(5)
        if (state == STATE_ME) { state = 0 }
        x = int(sin((double(Stage_frame\360)/180.0)*PI)*(MASS_H_NUM_DIV_2_MINUS_1))+MASS_H_NUM_DIV_2_PLUS_1
        repeat 9
            Objs(x+cnt\3-1, MASS_V_NUM_DIV_2+cnt/3-1, OBJ_STATE) = state
        loop
    }
    
    // ゲーム画面の準備
    if (Stage == STAGE_INIT) {
        if (Stage_frame < 20) { // 岩の生成
            repeat MASS_H_NUM, 1
                Objs(cnt, 1, OBJ_STATE) = STATE_ROCK
            loop
        } else : if (Stage_frame < 160) { // 山の生成
            repeat 3, -1
                Objs(MASS_H_NUM_DIV_2+cnt, MASS_V_NUM_DIV_4, OBJ_STATE) = STATE_ROCK
            loop
        } else : if (Stage_frame < 180) { // 水の生成
            repeat MASS_H_NUM_DIV_4, 1
                Objs(cnt, 1, OBJ_STATE) = STATE_WATER
                Objs(MASS_H_NUM-cnt, 1, OBJ_STATE) = STATE_WATER
            loop
        } else : if (Stage_frame == 180) { // 自機の生成
            Objs(MASS_H_NUM_DIV_2, MASS_V_NUM_DIV_4, OBJ_STATE) = STATE_ME
        }
        
        if (Stage_frame < 200) { // Ready, GO!!文字の表示
            font Font_name, 150
            pos 55, 105 : color 0, 0, 0
            mes Ready_cap
            pos 50, 100 : color 255, 0, 0
            mes Ready_cap
        } else : if (Stage_frame < 230) {
            pos 175, 105 : color 0, 0, 0
            mes Go_cap
            pos 170, 100 : color 255, 0, 0
            mes Go_cap
        } else {
            Stage = STAGE_PLAYING
            Stage_frame = 0
        }
    }
    
    // プレイ中の画面
    if (Stage == STAGE_PLAYING) {
        if (keys & 256) { // 左クリックで溶岩を生成
            repeat 3, -1
                Objs(mousex/MASS_W+cnt, mousey/MASS_H, OBJ_STATE) = STATE_LAVA
            loop
        }
    
        // スコアの表示
        font Font_name, 40
        pos 13, 13 : color 0, 0, 0
        mes Score_cap+Stage_frame
        pos 10, 10 : color 255, 0, 0
        mes Score_cap+Stage_frame
    
        if (Stage_frame\SCROLL_INTERVAL == 0) { // 画面のスクロール
            repeat MASS_H_NUM_MINUS_1, 1
                x = cnt
                repeat MASS_V_NUM, 1
                    Objs(x, cnt, OBJ_STATE) = Objs(x+1, cnt, OBJ_STATE)
                loop
            loop
            // 水を画面端に生成
            // 水不足にならないため
            Objs(MASS_H_NUM, MASS_V_NUM_DIV_2, OBJ_STATE) = STATE_WATER
        }
    }
    // GAME OVER画面
    if (Stage == STAGE_FINISH) {
        if (keys & 16) { // タイトルに戻る
            if (Stage_frame > Highscore) { Highscore = Stage_frame }
            Stage = STAGE_TITLE
            goto *init
        }
        
        font Font_name, 150
        pos 45, 55 : color 0, 0, 0
        mes Dead_reason
        pos 40, 50 : color 255, 0, 0
        mes Dead_reason
    
        font Font_name, 40
        pos 303, 303 : color 0, 0, 0
        mes Score_cap+Stage_frame
        pos 300, 300 : color 255, 0, 0
        mes Score_cap+Stage_frame
    
        font Font_name, 35
        pos 53, 403 : color 0, 0, 0
        mes Tryagain_cap
        pos 50, 400 : color 40, 175, 12
        mes Tryagain_cap
    }
    
goto *main


ちなみに以前の作品はこちら。

バブルソート(ページ中程) f:id:wait0000:20150823192145p:plain ただ単純にバブルソートを並列的に実行して、グラフィカルに表示しただけ。こちらは、HSPTVのおすすめプログラムに収録されているので、暇な人は実行して、心を研ぎすますといいと思う。

もっと暇な人は、別のソーティングアルゴリズム版を作ってみるといいと思う。