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分以内に何点稼げるかで競う感じにするとか。
  • 地面の上に蛇でも出すとか。プレイヤーキャラはジャンプで避けるとか。あるいは弾を撃って蛇を倒すとか。
  • フィーバータイムをつけるとか。何かを取ると一定時間、全てが林檎に変化したり。
  • ステージを設けるとか。ある程度取るとステージクリア。
  • 長くプレイしてると背景が変わってくるとか。徐々に夕焼けになったり、夜空になったり。

何にせよ、この段階では「変化」「ご褒美」が無いよなと…。ゲームにおいて「ご褒美」は大事。皆、何かしらの「ご褒美」が欲しくてプレイするわけだから…。

以上です。

過去ログ表示

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