HSPプログラミングコンテスト2014に応募した
激ムズ横スクロールアクションゲーム BE GODをHSPプログラミングコンテスト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
ちなみに以前の作品はこちら。
バブルソート(ページ中程) ただ単純にバブルソートを並列的に実行して、グラフィカルに表示しただけ。こちらは、HSPTVのおすすめプログラムに収録されているので、暇な人は実行して、心を研ぎすますといいと思う。
もっと暇な人は、別のソーティングアルゴリズム版を作ってみるといいと思う。