2018/11/13(火) [n年前の日記]
#1 [tic80] TIC-80でゲームっぽい何かを書いてみた
_TIC-80
を触り始めたことだし、せっかくだからゲームっぽい何かを書いてみようかなと。
しかし何を書こうか…。と悩んだところで、ふと、昔の _「るびま(Rubyist Magazine)」 で公開されていた、Ruby/SDL のサンプルゲームっぽいものを書いてみたらどうだろうと思いついたわけで。上から林檎 or 爆弾が落ちてくるので、プレイヤーキャラを操作して林檎だけキャッチする、というゲーム内容。
_Ruby/SDLで始めるゲームプログラミング【前編】
_Ruby/SDL で始めるゲームプログラミング【後編】
てなわけで、TIC-80上でも書いてみました。環境は、Windows10 x64 + TIC-80 0.70.6。
ブラウザ上で動かせます。以下をクリックすれば動くかなと。(音が出ます。)
_newtototone.tic.html
画像やソースを見たいときは、ブラウザ上で動かしてる状態でESCキーを押すとメニューが出てくるので、「CLOSE GAME」を選んでZキー(Aボタン)。その後、F1 〜 F5キーを叩けば、ソースや画像が見れるはず。
余談だけど。ローカルのTIC-80で動かした場合と、ブラウザ上で動かした場合で、疑似乱数の出方が違うようだなと…。ローカルで動くLuaと、ブラウザ上で動くJavaScriptは、疑似乱数の処理がそれぞれ違うのかもしれない…。
一応 .tic(TIC-80のファイル形式)も、 zip にして置いときます。
_newtototone_20181113.zip
しかし何を書こうか…。と悩んだところで、ふと、昔の _「るびま(Rubyist Magazine)」 で公開されていた、Ruby/SDL のサンプルゲームっぽいものを書いてみたらどうだろうと思いついたわけで。上から林檎 or 爆弾が落ちてくるので、プレイヤーキャラを操作して林檎だけキャッチする、というゲーム内容。
_Ruby/SDLで始めるゲームプログラミング【前編】
_Ruby/SDL で始めるゲームプログラミング【後編】
てなわけで、TIC-80上でも書いてみました。環境は、Windows10 x64 + TIC-80 0.70.6。
ブラウザ上で動かせます。以下をクリックすれば動くかなと。(音が出ます。)
_newtototone.tic.html
画像やソースを見たいときは、ブラウザ上で動かしてる状態でESCキーを押すとメニューが出てくるので、「CLOSE GAME」を選んでZキー(Aボタン)。その後、F1 〜 F5キーを叩けば、ソースや画像が見れるはず。
余談だけど。ローカルのTIC-80で動かした場合と、ブラウザ上で動かした場合で、疑似乱数の出方が違うようだなと…。ローカルで動くLuaと、ブラウザ上で動くJavaScriptは、疑似乱数の処理がそれぞれ違うのかもしれない…。
一応 .tic(TIC-80のファイル形式)も、 zip にして置いときます。
_newtototone_20181113.zip
◎ ソース。 :
一応ソースも貼り付けておくのです。
ちなみに、Lua はまだ全然勉強中なので、本来ならクラスの継承を使うべきところで使ってなかったり。精進します。
-- title: NEWTOTOTONE -- author: mieki256 -- desc: It's a game to catch an apple. -- script: lua scrw,scrh=240,136 Player={} Player.new=function() local obj={ alive=true,flip=0, x=scrw/2,y=scrh-32+8 } setmetatable(obj,{__index=Player}) return obj end Player.init=function(self) self.alive=true self.x=scrw/2 self.y=scrh-32+8 self.flip=0 end Player.update=function(self) if not self.alive then return end local spd=2 if btn(2) then self.x=self.x-spd self.flip=0 end if btn(3) then self.x=self.x+spd self.flip=1 end if self.x <= 8 then self.x=8 end if self.x >= scrw-8 then self.x=scrw-8 end end Player.draw=function(self) local id if self.alive then id=1+t%60//30*2 else id=9 self.flip=t%20//10 end local x,y=self.x-8,self.y-8 spr(id,x,y,14,1,self.flip,0,2,2) end Item={} Item.new=function(_x,_y,_kind) local obj={ alive=true, x=_x,y=_y,dx=0,dy=0, hit=false,kind=_kind, sprid=5 } if _kind==1 then obj.sprid=7 end setmetatable(obj,{__index=Item}) return obj end Item.update=function(self) self.x=self.x+self.dx self.y=self.y+self.dy self.dy=self.dy+0.3*9.8/60.0 if self.y >= scrh then self.y=-16 self.dy=0 self.alive=false end if self.hit then if self.kind==0 then score=score+10 bornInfo(self.x,self.y,10) end self.alive=false end end Item.draw=function(self) if self.alive then spr(self.sprid,self.x-8,self.y-8, 14,1,0,0,2,2) end end Info={} Info.new=function(_x,_y,_str) local obj={ alive=true, x=_x,y=_y,str=_str,timer=0 } setmetatable(obj,{__index=Info}) return obj end Info.update=function(self) self.y=self.y-0.5 self.timer=self.timer+1 if self.timer>30 then self.alive=false end end Info.draw=function(self) print(self.str,self.x+1,self.y+1,0) print(self.str,self.x,self.y,15) end function bornInfo(_x,_y,_score) local info=Info.new(_x,_y-16,"+".._score) table.insert(infos,info) end function hitcheck(p,objs) for i,spr in ipairs(objs) do local dx,dy,dist dx=math.abs(spr.x-p.x) dy=math.abs(spr.y-p.y) if spr.kind==0 then if dx<=8 and dy<=8 then spr.hit=true sfx(1, "C-5") end elseif spr.kind==1 then if (dx*dx+dy*dy)<(8*8) then spr.hit=true p.alive=false end end end end function updateSprites(objs) for i,spr in ipairs(objs) do spr:update() end end function removeSprites(objs) local elen=#objs for i=elen,1,-1 do if not objs[i].alive then table.remove(objs,i) end end end function drawSprites(objs) for i,spr in ipairs(objs) do spr:draw() end end function printCt(_s,_y,_scale) local w=print(_s,0,-6) local x=(scrw-(w*_scale))//2 local a=_scale print(_s,x+a,_y+a,0,false,_scale) print(_s,x,_y,15,false,_scale) end score=0 hiscore=0 gmstep=0 t=0 brate=100 player=Player.new() items={} infos={} PALADDR=0x3FC0+14*3 R1,G1,B1=0x59,0x7D,0xCE R2,G2,B2=0x6D,0xC2,0xCA function SCN(row) -- sky gradient local d,hh,h hh=scrh-8*4 h=hh*0.75 if row<h then d=0 elseif row>hh then d=1 else d=(row-h)/(hh-h) end poke(PALADDR,R1+(R2-R1)*d) poke(PALADDR+1,G1+(G2-G1)*d) poke(PALADDR+2,B1+(B2-B1)*d) end function TIC() if gmstep==0 then -- title if btnp(0) then gmstep=1 score=0 brate=100 music(0) math.randomseed(0) end elseif gmstep==1 then -- game main hitcheck(player,items) player:update() -- born item if t%60==0 then brate=brate-1 if brate<=1 then brate=100 end end if t%8==0 then local ex,b,k,item ex=math.random(8,scrw-8) b=math.max(20,brate) k=(math.random(100)>b) and 1 or 0 item=Item.new(ex,-16,k) table.insert(items,item) end updateSprites(items) updateSprites(infos) removeSprites(items) removeSprites(infos) if score>hiscore then hiscore=score end if not player.alive then gmstep=2 t=0 sfx(2,"C-5") music() -- music stop end elseif gmstep==2 then -- game over if t>=60+30 then player:init() gmstep=0 items={} infos={} end end -- draw cls(14) map(0,0,30,17,0,0,14) player:draw() drawSprites(items) drawSprites(infos) print("SCORE: "..score,4,2) print("HI-SCORE: "..hiscore,120,2) if gmstep==0 then printCt("NEWTOTOTONE",52,2) printCt("MOVE: LEFT,RIGHT KEY",84,1) printCt("PUSH UP KEY TO START",94,1) elseif gmstep==2 then printCt("GAME OVER",54,2) end t=t+1 end
- 〜.new が、クラスのインスタンス発生+初期化処理
- 〜.update が更新処理(1秒間に60回呼ばれる)
- 〜.draw が描画処理
ちなみに、Lua はまだ全然勉強中なので、本来ならクラスの継承を使うべきところで使ってなかったり。精進します。
◎ 宿題。 :
これだけではゲームとして全然面白くないよなと…。プレイしていて、なんだか修行僧になった気分…。
さて、どうすれば、ちょっとは面白くなるのだろうか…。
例えばだけど…。
何にせよ、この段階では「変化」「ご褒美」が無いよなと…。ゲームにおいて「ご褒美」は大事。皆、何かしらの「ご褒美」が欲しくてプレイするわけだから…。
さて、どうすれば、ちょっとは面白くなるのだろうか…。
例えばだけど…。
- 時間制限をつけて、1分以内に何点稼げるかで競う感じにするとか。
- 地面の上に蛇でも出すとか。プレイヤーキャラはジャンプで避けるとか。あるいは弾を撃って蛇を倒すとか。
- フィーバータイムをつけるとか。何かを取ると一定時間、全てが林檎に変化したり。
- ステージを設けるとか。ある程度取るとステージクリア。
- 長くプレイしてると背景が変わってくるとか。徐々に夕焼けになったり、夜空になったり。
何にせよ、この段階では「変化」「ご褒美」が無いよなと…。ゲームにおいて「ご褒美」は大事。皆、何かしらの「ご褒美」が欲しくてプレイするわけだから…。
[ ツッコむ ]
#2 [tic80] TIC-80で画面キャプチャ
TIC-80を動かしていて、動作画面をキャプチャしたいと思った際、F9キーを押すとGIFアニメとしてキャプチャできると知ったのでメモ。
F9キーを叩くと、画面の右上のほうに、記録中のアイコンが表示される。
一定時間キャプチャするとファイル保存ダイアログが開いて、GIFアニメとして保存できる。
キャプチャ時間は、config を弄ることで変更できるっぽい。入力待ち画面で config[Enter] と打つと設定ファイルがロードされて、F1キーを押してエディタを開くと設定ファイルを開くことができる。
以下が、関係してる記述だろうか。(TIC-80 0.70.6 の場合。)
出力されたGIFアニメの各フレームには、2/100secのwait値が入ってた。60FPSなら1フレーム約16.67msになるので、GIFアニメに指定できるwait値としては妥当な気がする。
ただ、出力ファイルサイズが異様に大きい。そのままだと、10MBぐらいのGIFアニメになってしまった。 _EZGIF.COM を利用させてもらって、不要なフレームの削除、及び、最適化をしたら、810KBぐらいになった。
ちなみに、実行画面を _OBS Studio を使ってデスクトップ画面ごとキャプチャして、切り出して mp4 にしてみたところ、約900KBぐらいになった。mp4 なら音声まで入ってることを考えると、GIFアニメ以外にも mp4 にしてblog等に貼る選択肢もアリなのかもしれないなと。
F9キーを叩くと、画面の右上のほうに、記録中のアイコンが表示される。
一定時間キャプチャするとファイル保存ダイアログが開いて、GIFアニメとして保存できる。
キャプチャ時間は、config を弄ることで変更できるっぽい。入力待ち画面で config[Enter] と打つと設定ファイルがロードされて、F1キーを押してエディタを開くと設定ファイルを開くことができる。
以下が、関係してる記述だろうか。(TIC-80 0.70.6 の場合。)
GIF_LENGTH=20 -- in seconds GIF_SCALE=2
- GIF_LENGTH が、キャプチャする時間なのだろう…。たぶん。デフォルトは20秒。
- GIF_SCALE が、キャプチャ時の画面の拡大率、なのかな。デフォルトでは2倍に拡大した状態でキャプチャするらしい。
出力されたGIFアニメの各フレームには、2/100secのwait値が入ってた。60FPSなら1フレーム約16.67msになるので、GIFアニメに指定できるwait値としては妥当な気がする。
ただ、出力ファイルサイズが異様に大きい。そのままだと、10MBぐらいのGIFアニメになってしまった。 _EZGIF.COM を利用させてもらって、不要なフレームの削除、及び、最適化をしたら、810KBぐらいになった。
ちなみに、実行画面を _OBS Studio を使ってデスクトップ画面ごとキャプチャして、切り出して mp4 にしてみたところ、約900KBぐらいになった。mp4 なら音声まで入ってることを考えると、GIFアニメ以外にも mp4 にしてblog等に貼る選択肢もアリなのかもしれないなと。
[ ツッコむ ]
#3 [prog] 「Hello world」の次の課題の候補が欲しい気もする
思考メモ。
プログラミングの世界には、「Hello world」というお題があって。
「コンソール上(あるいはウインドウ内)に、『Hello World』と表示してみよう!」というお題なのだけど。自分が知らない言語やライブラリを使う際には、まずは「Hello world」をやってみる、というのがよくある流れ、だったりするわけで。
非プログラマーさんからは、「えっ? たったそれだけの課題なの?」と思われるかもしれないけれど。コレ、意外と大事な課題で。
「Hello world」をするためには、開発環境の構築・導入が必要で。コンパイルをする言語なら、コンパイラだの何だのをインストールしなきゃいけない。つまり、「Hello world」と表示できた段階で、「これで開発環境の構築は終わったよ」「さあ、後は各自、目的のプログラムを書くだけだよ」という状態になる。「Hello world」すらできないようでは、その先のプログラミングもへったくれもないのです。
しかし…。「Hello world」が終わった段階で、「さあ、環境構築はできたぞ。…さて、ここから一体何を書こうかなあ。どうしよう」と悩んじゃうときも…あるよなと。
「そんな馬鹿な。何かを作りたくて環境構築したんでしょ? その何かを書き始めればいいじゃん」と思われるかもしれないけれど。意外とそういう流れじゃない時もあって…。「お? なんだかコレ面白そうだな。少し触ってみるか」なんて動機で、自分にとっては未知の言語・ライブラリを試してみる、てな時もあるわけですよ。
そういうときに、ちょっと困っちゃう。次の段階に進みたいけど、適切なお題が思いつかないのだよなあ。みたいな。
そういったことを考えると、 _「るびま(Rubyist Magazine)」 で紹介されてた、Ruby/SDLのサンプルゲームの仕様 ―― 上から落ちてくる何かをひたすら拾うというゲーム内容は、ゲームプログラミングのお題としては結構イイ感じかもしれない、と。
_Ruby/SDLで始めるゲームプログラミング【前編】
_Ruby/SDL で始めるゲームプログラミング【後編】
このサンプル相当を作れば…。
他にも、ヨサゲなお題はないものだろうか…。
自分の場合はゲームプログラミングについて考えちゃうけど、例えばWebサービス作成版の、「Hello world」の次のお題、とかあっても良さそうだよな…。
電子工作なら、Lチカ(LEDをチカチカさせるアレ)の次のお題、とか。
もっとも、見るからに面白そうと感じられたり、あるいは発展させるとこうなりそう的な想像ができるお題じゃないと、モチベーションが出てこないか…。
プログラミングの世界には、「Hello world」というお題があって。
「コンソール上(あるいはウインドウ内)に、『Hello World』と表示してみよう!」というお題なのだけど。自分が知らない言語やライブラリを使う際には、まずは「Hello world」をやってみる、というのがよくある流れ、だったりするわけで。
非プログラマーさんからは、「えっ? たったそれだけの課題なの?」と思われるかもしれないけれど。コレ、意外と大事な課題で。
「Hello world」をするためには、開発環境の構築・導入が必要で。コンパイルをする言語なら、コンパイラだの何だのをインストールしなきゃいけない。つまり、「Hello world」と表示できた段階で、「これで開発環境の構築は終わったよ」「さあ、後は各自、目的のプログラムを書くだけだよ」という状態になる。「Hello world」すらできないようでは、その先のプログラミングもへったくれもないのです。
しかし…。「Hello world」が終わった段階で、「さあ、環境構築はできたぞ。…さて、ここから一体何を書こうかなあ。どうしよう」と悩んじゃうときも…あるよなと。
「そんな馬鹿な。何かを作りたくて環境構築したんでしょ? その何かを書き始めればいいじゃん」と思われるかもしれないけれど。意外とそういう流れじゃない時もあって…。「お? なんだかコレ面白そうだな。少し触ってみるか」なんて動機で、自分にとっては未知の言語・ライブラリを試してみる、てな時もあるわけですよ。
そういうときに、ちょっと困っちゃう。次の段階に進みたいけど、適切なお題が思いつかないのだよなあ。みたいな。
そういったことを考えると、 _「るびま(Rubyist Magazine)」 で紹介されてた、Ruby/SDLのサンプルゲームの仕様 ―― 上から落ちてくる何かをひたすら拾うというゲーム内容は、ゲームプログラミングのお題としては結構イイ感じかもしれない、と。
_Ruby/SDLで始めるゲームプログラミング【前編】
_Ruby/SDL で始めるゲームプログラミング【後編】
このサンプル相当を作れば…。
- ウインドウの表示。
- 画像の表示(描画)。
- キー入力でプレイヤーキャラを動かす。
- 複数のオブジェクトの発生・移動・描画。
- オブジェクトとプレイヤーキャラのアタリ判定。
- 音楽(BGM)の再生。
- 効果音(SFX)の再生。
他にも、ヨサゲなお題はないものだろうか…。
自分の場合はゲームプログラミングについて考えちゃうけど、例えばWebサービス作成版の、「Hello world」の次のお題、とかあっても良さそうだよな…。
電子工作なら、Lチカ(LEDをチカチカさせるアレ)の次のお題、とか。
もっとも、見るからに面白そうと感じられたり、あるいは発展させるとこうなりそう的な想像ができるお題じゃないと、モチベーションが出てこないか…。
◎ 昔見かけたお題。 :
某社の新人研修時に出されたお題も、基本を学ぶ分にはなんだか良さそうだな、と思い出したりして。
この課題をやると、何が身につくのだろう…。
もっとも、今時はそのあたりフレームワーク云々で用意されてそうな気もする。今時の環境に合ったお題となると、どういった内容になるのだろう。
- 横スクロールシューティングゲームを作れ。
- 自機はカーソルキーで上下左右に動く。
- 自機は弾を撃てる。
- 雑魚敵は画面内に複数出てくる。
- 雑魚敵は自機の弾で消せる。
- 雑魚敵を4種類実装しろ。
- 雑魚敵Aは、画面右から出現。sinカーブで飛んでくる。
- 雑魚敵Bは、画面右から出現。自機を追いかける。
- 雑魚敵Cは、画面左から出現。放物線を描いて画面左に帰っていく。
- 雑魚敵Dは、(仕様失念)
この課題をやると、何が身につくのだろう…。
- 画面内に複数のオブジェクトを出して動かす仕組み、の基本を学べる。
- 速度と加速度の使い方を学ぶ。
- 角度から速度を求めて使うやり方を知る。(三角関数の復習。)
もっとも、今時はそのあたりフレームワーク云々で用意されてそうな気もする。今時の環境に合ったお題となると、どういった内容になるのだろう。
*1: まあ、研修を受けていたほとんどの人は数日で課題をクリアして、残り時間は各々が好き勝手な仕様を追加して遊んでたけど…。
[ ツッコむ ]
以上、1 日分です。