pythonでCLIで動くPongを作ってみた
こんにちは、cacaponです😄
今日は、CLI上で動くPongを作ったので
その時の話をしようかと思います✨
そもそもPongって何?
1972年にアタリ社が発売したゲームで
一般的には一番最初のテレビゲームとして普及したようです
CLIってなんぞや?
Comand Line Interface の略で
コマンドプロンプトやBash,PowerShellやMacのコンソールのように
文字入力で動かすインターフェースの事を指します CUI*2とも言います
対となすものとしてGUIがあります。
Windowsとかのクリックとか、スマホのアプリをタップで開くなんかもGUIですね。
通常のCLIのメリットとしては、コマンドの組み合わせで複雑な処理を一括で行えることが挙げられます*3
それゆえ、プログラマーやコアなパソコンユーザーの方はCLIを好む方も多いです。
ゲームの場合だと、実装がめんどくさいグラフィカルな部分を簡単な文字出力で実現できるので
基本的なゲームを作るのにはとても向いていると私は考えています😄
pythonでつくったPongはこちら
ちょっとちらつきが気になりますかね…😅
どんな感じで作ったの?
元々はUE4でPongを作ろうと思っていたのですが、
UE4自体の知見があまり多くは無い状態でしたので、
とりあえず慣れているPythonでコードベースで作ってみようと思ったのがきっかけです。
作り方としては、必要な素材をリストアップしました。
今回は以下のような感じ🙄
- ステージ
- ボール
- 壁
- 得点
- パドル
あとは、行き当たりばったりってとりあえず動くように 一個ずつ実装してきました。
ソースコード
折りたたんでいます
import os import msvcrt import random STAGE_HEIGHT = 15 STAGE_WIDTH = 100 STAGE = [] BALL_VEC_TYPE = { 'UL': [-1, -1], 'UR': [-1, 1], 'DL': [1, -1], 'DR': [1, 1] } BALL_VEC = 'UL' BALL_POS = [STAGE_HEIGHT // 2, STAGE_WIDTH // 2] POINT = [0, 0] def init_stage(): STAGE.append(list('-' * STAGE_WIDTH)) for i in range(STAGE_HEIGHT - 2): STAGE.append(list(' ' * STAGE_WIDTH)) STAGE.append(list('-' * STAGE_WIDTH)) def set_puddle(pos, is_player): x_pos = 1 if is_player else STAGE_WIDTH - 2 # 一旦|を消す for row in STAGE: if row[x_pos] == '|': row[x_pos] = ' ' STAGE[pos - 1][x_pos] = '|' STAGE[pos][x_pos] = '|' STAGE[pos + 1][x_pos] = '|' def draw_stage(): os.system('cls') # PowerShellの場合。bashで動かす場合はclearにする print(' ' * (STAGE_WIDTH // 2 - 5), POINT, ' ' * (STAGE_WIDTH // 2 - 5)) for row in STAGE: print(''.join(row)) def realtime_input(): cmd = ['w', 's', 'q'] if msvcrt.kbhit(): # キーが押されているか key = msvcrt.getch().decode() # 押されていれば、キーを取得する return key if key in cmd else ' ' else: return ' ' def puddle_move(key): if key == 'w': return -1 if key == 's': return 1 return 0 def AI_puddle_move(pos): random_num = random.random() if random_num < 0.33: return 1 if random_num < 0.67: return 0 else: return -1 def puddle_fix_pos(pos): # 1 ~ HEIGHT -1 if pos <= 1: return 2 if pos >= STAGE_HEIGHT - 2: return STAGE_HEIGHT - 3 return pos def ball_move(): # 45°に動く # 移動先のパターン # ' ' 移動 # '-' 反射 # '|' 反射 # '両端' 得点 global BALL_VEC global BALL_VEC_TYPE global BALL_POS global POINT vec = BALL_VEC_TYPE[BALL_VEC] moved_pos = [ BALL_POS[0] + vec[0], BALL_POS[1] + vec[1], ] if moved_pos[1] == STAGE_WIDTH - 1: POINT[0] += 1 STAGE[BALL_POS[0]][BALL_POS[1]] = ' ' BALL_POS = [STAGE_HEIGHT // 2, STAGE_WIDTH // 2] BALL_VEC = 'UR' return elif moved_pos[1] == 0: POINT[1] += 1 STAGE[BALL_POS[0]][BALL_POS[1]] = ' ' BALL_POS = [STAGE_HEIGHT // 2, STAGE_WIDTH // 2] BALL_VEC = 'UL' return if STAGE[moved_pos[0]][moved_pos[1]] == ' ': STAGE[BALL_POS[0]][BALL_POS[1]] = ' ' STAGE[moved_pos[0]][moved_pos[1]] = 'o' BALL_POS = moved_pos return elif STAGE[moved_pos[0]][moved_pos[1]] == '-': if BALL_VEC == 'UR': BALL_VEC = 'DR' elif BALL_VEC == 'UL': BALL_VEC = 'DL' elif BALL_VEC == 'DR': BALL_VEC = 'UR' elif BALL_VEC == 'DL': BALL_VEC = 'UL' elif STAGE[moved_pos[0]][moved_pos[1]] == '|': if BALL_VEC == 'UR': BALL_VEC = 'UL' elif BALL_VEC == 'UL': BALL_VEC = 'UR' elif BALL_VEC == 'DR': BALL_VEC = 'DL' elif BALL_VEC == 'DL': BALL_VEC = 'DR' else: Exception('想定外') def main(): init_stage() puddle_pos_1p = 7 puddle_pos_2p = 7 while True: draw_stage() key = realtime_input() if key == 'q': break # update # 1p controller puddle_pos_1p += puddle_move(key) puddle_pos_1p = puddle_fix_pos(puddle_pos_1p) set_puddle(puddle_pos_1p, True) # AI controller puddle_pos_2p += AI_puddle_move(puddle_pos_2p) puddle_pos_2p = puddle_fix_pos(puddle_pos_2p) set_puddle(puddle_pos_2p, False) # Ball ball_move() print('Thank you for Playing') if __name__ == '__main__': main()
最後に
実際に作ってみて、どんな要素が必要か分かったかなぁと思います😄
勉強になりました✨
今度はUE4で実装してみようと思いますが、その前にクラスベースで一度作り直した方がいいかも💦
それではまた😆
参考記事
*1:ほんとは卓球らしいですけど
*2:Character User Interface の略
*3:例えば指定の階層に一気に移動したり、
複数のファイル名を一括変更したり
あるファイルを読み込んでプログラムを実行するなども出来ます。
最も私もにわかCLI使いなので、私がし倣いもっとすごいことも出来ると思います