タイニーなあやつで、簡単なゲームを…(後編)
前回の記事はこちらになります~ cacapon.hatenablog.com
前回の記事で足りていなかった情報を書いていきますね~
まず、今回のゲームはこちらの最初の課題を元にしています~
こちらの本は、C++を題材にしていますが、
今回はpythonで実装したので、
ヒントとして使ったのはこちらになります。
// ゲームは私の知る限り一つの例外もなくこの構造とのこと。 while ( true ){ getInput(); updateGame(); draw(); }
ステージデータ
######## # .. p # # oo # # # ########
これを元に、Tiny Core Linuxで動くゲーム
Mr.Carrierの作成に取り掛かりました。
そして完成したのがこちら。
矢印キーで移動して、oを.の所まで押していく感じですね~
CTRL+Cで終了、CTRL+Rで場面のリセットができるようになってます♪
結局丸一日くらいで完成なので、
初心者レベルでしょうか…
(本によると新入社員さんでおおよそ1日~2日で完成だそうです)
コードはこんな感じで作りました~
フォルダ構造
mr_carrier |--draw.py |--getch.py |--getinput.py |--listfind.py |--main.py |--stage_data.txt |--updategame.py
main.py
大まかな流れをこちらに書きました~
from getinput import GetInput as GetI from updategame import UpdateGame as UpG from draw import Draw as D UpG.init_stage_data() UpG.stage_error_ck() D.draw() while UpG.clear_flg(): get_key = GetI.getinput() if get_key == 'exit': break UpG.update_game(get_key) D.draw() print('Thank you for playing!')
from subprocess import call from updategame import UpdateGame as UpG class Draw(): @staticmethod def draw(): call('clear') print('Hi! Mr.Carrier!') print('Could you carry my luggage up to . ?') print('') print('=====Rule=====') print('1.You can move with the direction key.') print('2.You can push luggage') print(' But only one luggage can be pushed.') print('3.You should carry all luggage to . ') print('====Command===') print('[CTRL+C : EXIT] [CTRL+R : RESET GAME]') print('') # struct stage data. for line in UpG.stage_data: print(''.join(line))
class GetCh(): @staticmethod def getch(): try: from msvcrt import getch return getch() except ImportError: import sys import tty import termios fd = sys.stdin.fileno() old = termios.tcgetattr(fd) try: tty.setraw(fd) return sys.stdin.read(1) finally: termios.tcsetattr(fd, termios.TCSADRAIN, old)
from getch import GetCh as G key_list = { '65': 'up', #vscode:72 '66': 'down', #vscode:80 '67': 'right', #vscode:77 '68': 'left', #vscode:75 '3': 'exit', '18': 'reset', # vector key is 27 + 91 + X. # 27 and 91 is ignore '27': 'ignore', '91': 'ignore' } class GetInput(): @staticmethod def getinput(): get_key = ord(G.getch()) return key_list.get(str(get_key))
class ListFind(): @staticmethod def listfind(list, words): # lower and upper measures. local_list = [] for value in list: local_list.append(value.lower()) try: num = local_list.index(words.lower()) return num except ValueError: return None
updategame.py
今回一番大変だったところです~
もうちょっとスマートにできたらよかったなぁ…
import copy from listfind import ListFind as Lfind # game data update. class UpdateGame(): stage_data = [] cnt_dot = 0 cnt_obj = 0 cnt_p = 0 @classmethod def init_stage_data(cls): cls.stage_data = [] cls.cnt_dot = 0 cls.cnt_obj = 0 cls.cnt_p = 0 with open('stage_data.txt', mode='r') as f: for line in f: list_line = list(line.rstrip()) cls.stage_data.append(list_line) cls.cnt_dot += list_line.count('.') cls.cnt_obj += list_line.count('o') cls.cnt_p += list_line.count('p') @classmethod def stage_error_ck(cls): if cls.cnt_dot != cls.cnt_obj: raise ValueError('goal and object is not same count.') if cls.cnt_p != 1: raise ValueError('Player is not one people. {}'.format(cls.cnt_p)) @classmethod def clear_flg(cls): # not yet... luggage_on_goal = 0 for row in cls.stage_data: luggage_on_goal += row.count('O') if cls.cnt_obj == luggage_on_goal: print('Game Clear!!') print('Thank you bery match. It was saved!') return False return True @staticmethod def update_game(key): # key -> move player if key == 'ignore': return if key == None: #wait. return if key == 'reset': UpdateGame.init_stage_data() return UpdateGame.player_action(key) @classmethod def player_action(cls, direction): player = Player(direction) player.set_player_pos(cls.stage_data) player.set_move_pos(direction) player.move(cls.stage_data) @classmethod def luggage_action(cls, my_pos, direction): luggage = Luggage(my_pos) luggage.set_move_pos(direction) luggage.move(cls.stage_data) class Common(): def __init__(self): self.bef_pos = {'y': None, 'x': None} self.aft_pos = {'y': None, 'x': None} self.dict_direction = { 'up' : {'y': -1, 'x': 0}, 'down' : {'y': 1, 'x': 0}, 'left' : {'y': 0, 'x': -1}, 'right' : {'y': 0, 'x': 1} } def set_move_pos(self, direct): # {'x':?, 'y':?} + {'x':?, 'y':?} self.aft_pos = {'x' : None, 'y': None} self.aft_pos['x'] = self.bef_pos['x'] + self.dict_direction[direct]['x'] self.aft_pos['y'] = self.bef_pos['y'] + self.dict_direction[direct]['y'] def move(self, stage_data, obj): if stage_data[self.aft_pos['y']][self.aft_pos['x']] == '#': # not move return if (stage_data[self.aft_pos['y']][self.aft_pos['x']] == ' ' or stage_data[self.aft_pos['y']][self.aft_pos['x']] == '.'): # move self.bef_check(stage_data) self.aft_check(stage_data, obj) def bef_check(self, stage_data): if (stage_data[self.bef_pos['y']][self.bef_pos['x']] == 'O' or stage_data[self.bef_pos['y']][self.bef_pos['x']] == 'P'): stage_data[self.bef_pos['y']][self.bef_pos['x']] = '.' else: stage_data[self.bef_pos['y']][self.bef_pos['x']] = ' ' def aft_check(self, stage_data, obj): if stage_data[self.aft_pos['y']][self.aft_pos['x']] == '.': stage_data[self.aft_pos['y']][self.aft_pos['x']] = obj.upper() else: stage_data[self.aft_pos['y']][self.aft_pos['x']] = obj class Player(Common): def __init__(self, direction): super(Player, self).__init__() self.direction = direction def set_player_pos(self, stage_data): for r,row in enumerate(stage_data): if Lfind.listfind(row, 'p') is not None : self.bef_pos['y'] = r self.bef_pos['x'] = Lfind.listfind(row, 'p') break def move(self, stage_data): if (stage_data[self.aft_pos['y']][self.aft_pos['x']] == 'o' or stage_data[self.aft_pos['y']][self.aft_pos['x']] == 'O'): # put luggage UpdateGame().luggage_action(self.aft_pos, self.direction) return super().move(stage_data, 'p') class Luggage(Common): def __init__(self, my_pos): super().__init__() self.bef_pos = my_pos def move(self, stage_data): if (stage_data[self.aft_pos['y']][self.aft_pos['x']] == 'o' or stage_data[self.aft_pos['y']][self.aft_pos['x']] == 'O'): # not move return super().move(stage_data, 'o')
stage_data.txt
ステージのデータです。ここを変えればステージを変えることができます。
updategeme.pyでステージのエラーチェックもしていますが、
①荷物とゴールの数が一致しているか?
②プレイヤーが一人になっているか?
のチェックしかしていないので、
囲いがちゃんとしていなかったり、
指定の文字列以外が入っていたりすると、
うまく動かなくなりそうです~
######## # .. p # # oo # # # ########
大変だったこと
- drawやkey_inputは比較的すんなりできたのですが、
updateの部分でどのように実装するか悩みました~ - 一応遊べるところまでできたのは良かったですが、
セクション内の記述にもある通り、ちょこちょこバグが起きそうなところとか、
思ったより長くなってしまったところ、
理想形が分からなく妥協しているところもあるので、
そこは課題になりそうです~ - Tiny Core Linuxを日本語対応させてなかったので、日本語が使えませんでした…
- 新PCに戻したとき、動かなくなったこと。
→文字コードtarminalとwindows?で違ったみたいで矢印キーが認識されませんでした~
こういうところで移植の問題があるんだなぁと痛感しました~
まとめ
思ったより大変なところもありましたが、
総じて楽しめてゲーム作りができたかなぁと思います♪
でも、これが一番最初の課題なんですよね…
そして、現在画像すら出てきてないレベルです~
ここから少しでもレベルアップをして
今出ているゲームに近い物が作れるようになりたいなぁ
と思うcacaponでした~