mieki256's diary



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_ss2.gif

ブラウザ上で動かせます。以下をクリックすれば動くかなと。(音が出ます。)

_newtototone.tic.html

画像やソースを見たいときは、ブラウザ上で動かしてる状態でESCキーを押すとメニューが出てくるので、「CLOSE GAME」を選んでZキー(Aボタン)。その後、F1 〜 F5キーを叩けば、ソースや画像が見れるはず。

余談だけど。ローカルのTIC-80で動かした場合と、ブラウザ上で動かした場合で、疑似乱数の出方が違うようだなと…。ローカルで動くLuaと、ブラウザ上で動くJavaScriptは、疑似乱数の処理がそれぞれ違うのかもしれない…。

一応 .tic(TIC-80のファイル形式)も、 zip にして置いときます。

_newtototone_20181113.zip

ソース。 :

一応ソースも貼り付けておくのです。

-- 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キーを叩くと、画面の右上のほうに、記録中のアイコンが表示される。

tic80_gifanime_cap_mark_ss.gif

一定時間キャプチャするとファイル保存ダイアログが開いて、GIFアニメとして保存できる。

キャプチャ時間は、config を弄ることで変更できるっぽい。入力待ち画面で config[Enter] と打つと設定ファイルがロードされて、F1キーを押してエディタを開くと設定ファイルを開くことができる。

以下が、関係してる記述だろうか。(TIC-80 0.70.6 の場合。)
GIF_LENGTH=20 -- in seconds
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をチカチカさせるアレ)の次のお題、とか。

もっとも、見るからに面白そうと感じられたり、あるいは発展させるとこうなりそう的な想像ができるお題じゃないと、モチベーションが出てこないか…。

昔見かけたお題。 :

某社の新人研修時に出されたお題も、基本を学ぶ分にはなんだか良さそうだな、と思い出したりして。
  • 横スクロールシューティングゲームを作れ。
  • 自機はカーソルキーで上下左右に動く。
  • 自機は弾を撃てる。
  • 雑魚敵は画面内に複数出てくる。
  • 雑魚敵は自機の弾で消せる。
  • 雑魚敵を4種類実装しろ。
  • 雑魚敵Aは、画面右から出現。sinカーブで飛んでくる。
  • 雑魚敵Bは、画面右から出現。自機を追いかける。
  • 雑魚敵Cは、画面左から出現。放物線を描いて画面左に帰っていく。
  • 雑魚敵Dは、(仕様失念)
こんな感じの課題…だった気がする。大昔のことだからちょっと記憶が怪しいけれど。たしか、PC-9801上でスプライト相当を描画できるライブラリと、自機、弾、雑魚敵のドット絵データを渡されて、この課題を2週間でクリアせよ、みたいな。 *1

この課題をやると、何が身につくのだろう…。
  • 画面内に複数のオブジェクトを出して動かす仕組み、の基本を学べる。
  • 速度と加速度の使い方を学ぶ。
  • 角度から速度を求めて使うやり方を知る。(三角関数の復習。)
そんな感じなのかな…。

もっとも、今時はそのあたりフレームワーク云々で用意されてそうな気もする。今時の環境に合ったお題となると、どういった内容になるのだろう。

*1: まあ、研修を受けていたほとんどの人は数日で課題をクリアして、残り時間は各々が好き勝手な仕様を追加して遊んでたけど…。

以上、1 日分です。

過去ログ表示

Prev - 2018/11 - Next
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30

カテゴリで表示

検索機能は Namazu for hns で提供されています。(詳細指定/ヘルプ


注意: 現在使用の日記自動生成システムは Version 2.19.6 です。
公開されている日記自動生成システムは Version 2.19.5 です。

Powered by hns-2.19.6, HyperNikkiSystem Project