2018/11/14(水) [n年前の日記]
#1 [tic80] TIC-80でシューティングゲームっぽいものを書いてみようかなと
せっかくだからTIC-80上で横スクロールシューティングっぽいものを書いてみようかなと。環境は、Windows10 x64 + TIC-80 0.70.6。
◎ 自機の移動。 :
まずは、自機の移動処理。カーソルキーでスプライトを動かすあたりを書いてみる。ついでに、後々のことも考えて、Playerクラスを作って分離してみる。

TIC-80上で new と打てば Hello world相当がロードされた状態になるので、F1キーを押してエディタを表示してソースを眺めれば、カーソルキーの入力や、スプライト描画のやり方はすぐわかるのだけど…。
8方向に動かす際には、斜め移動の距離について、ちょっと注意しないといけない。詳しくは以下を参考に…。
_アナログ入力の斜め補正
_第5回 キー入力によってキャラクタを8方向へ動かす | TAKABO SOFT
_斜めに移動させる | ゲームプログラミング入門〜bituse〜
とりあえず今回は、斜め移動の時だけ、math.cos(math.rad(45)) を掛けた値を自機の移動量にしてみたり。
ちなみに以下の行は、画面外に自機が出ていかないようにしている。

-- title: stg sample 1
-- author: mieki256
-- desc: short description
-- script: lua
scrw,scrh=240,136
-- ----------------------------------------
-- Player class
Player={}
Player.new=function()
local o={
alive=true,
x=scrw/2,
y=scrh/2,
sprid=5
}
setmetatable(o,{__index=Player})
return o
end
Player.update=function(self)
local spd=2.0
local dx,dy=0,0
if btn(0) then dy=-spd end
if btn(1) then dy=spd end
if btn(2) then dx=-spd end
if btn(3) then dx=spd end
if dx~=0 and dy~=0 then
local d=math.cos(math.rad(45))
dx=dx*d
dy=dy*d
end
self.x=self.x+dx
self.y=self.y+dy
self.x=math.min(math.max(self.x,8),scrw-8)
self.y=math.min(math.max(self.y,8),scrh-8)
end
Player.draw=function(self)
local x,y=self.x-8,self.y-8
spr(self.sprid,x,y,14,1,0,0,2,2)
end
-- init
score=0
t=0
player=Player.new()
-- main loop
function TIC()
player:update()
cls(0)
player:draw()
print("SCORE: "..score,2,2)
t=t+1
end
TIC-80上で new と打てば Hello world相当がロードされた状態になるので、F1キーを押してエディタを表示してソースを眺めれば、カーソルキーの入力や、スプライト描画のやり方はすぐわかるのだけど…。
8方向に動かす際には、斜め移動の距離について、ちょっと注意しないといけない。詳しくは以下を参考に…。
_アナログ入力の斜め補正
_第5回 キー入力によってキャラクタを8方向へ動かす | TAKABO SOFT
_斜めに移動させる | ゲームプログラミング入門〜bituse〜
とりあえず今回は、斜め移動の時だけ、math.cos(math.rad(45)) を掛けた値を自機の移動量にしてみたり。
if dx~=0 and dy~=0 then local d=math.cos(math.rad(45)) dx=dx*d dy=dy*d end
ちなみに以下の行は、画面外に自機が出ていかないようにしている。
self.x=math.min(math.max(self.x,8),scrw-8) self.y=math.min(math.max(self.y,8),scrh-8)math.min(v1, v2) は最小値、math.max(v1, v2) は最大値を取得できる。 _Lua言語のライブラリ関数 の、 _math.min 、 _math.max が参考になる、かなと。
◎ 弾を撃つ。 :
自機から弾を撃ってみる。弾を担当する Bulletクラスを作って、一定時間毎に発生させる。

Bulletクラスの他にも、複数のオブジェクトを処理する関数、objsUpdate()、objsDraw()、objsRemove() を書いた。また、プレイヤーの更新処理(update())の中で、一定時間毎に弾を発生させる処理を追加した。
弾は、初期座標、速度、角度(方向)を与えて発生させている。弾は、その方向にまっすぐ進んで、画面外に出たら自分の存在フラグ(?)を false にする。後で、objsRemove() が、存在フラグが false になってるインスタンスを探して削除してくれる。

-- title: stg sample 2
-- author: mieki256
-- desc: short description
-- script: lua
scrw,scrh=240,136
-- ---------------------------------------
-- Player class
Player={}
Player.new=function()
local o={
alive=true,
x=scrw/2,
y=scrh/2,
sprid=5
}
setmetatable(o,{__index=Player})
return o
end
Player.update=function(self)
local spd=2.0
local dx,dy=0,0
if btn(0) then dy=-spd end
if btn(1) then dy=spd end
if btn(2) then dx=-spd end
if btn(3) then dx=spd end
if dx~=0 and dy~=0 then
local d=math.cos(math.rad(45))
dx=dx*d
dy=dy*d
end
self.x=self.x+dx
self.y=self.y+dy
self.x=math.min(math.max(self.x,8),scrw-8)
self.y=math.min(math.max(self.y,8),scrh-8)
-- shot
if t%6==0 then
local x,y=self.x,self.y
spd=6
table.insert(bullets,Bullet.new(x,y,spd,0))
table.insert(bullets,Bullet.new(x,y,spd,12))
table.insert(bullets,Bullet.new(x,y,spd,-12))
table.insert(bullets,Bullet.new(x,y,spd,180))
end
end
Player.draw=function(self)
local x,y=self.x-8,self.y-8
spr(self.sprid,x,y,14,1,0,0,2,2)
end
-- ---------------------------------------
-- Bullet class
Bullet={}
Bullet.new=function(_x,_y,_spd,_ang)
local o={
alive=true,
x=_x,y=_y,spd=_spd,ang=_ang,
sprid=7
}
local ra=math.rad(_ang)
o.dx=_spd*math.cos(ra)
o.dy=_spd*math.sin(ra)
setmetatable(o,{__index=Bullet})
return o
end
Bullet.update=function(self)
self.x=self.x+self.dx
self.y=self.y+self.dy
if self.x<-16 or self.x>scrw+16 or
self.y<-16 or self.y>scrh+16 then
self.alive=false
end
end
Bullet.draw=function(self)
local x,y=self.x-4,self.y-4
spr(self.sprid,x,y,0,1,0,0,1,1)
end
-- ----------------------------------------
function objsUpdate(objs)
for i,o in ipairs(objs) do
o:update()
end
end
function objsDraw(objs)
for i,o in ipairs(objs) do
o:draw()
end
end
function objsRemove(objs)
local l=#objs
for i=l,1,-1 do
if not objs[i].alive then
table.remove(objs,i)
end
end
end
-- init
score=0
t=0
player=Player.new()
bullets={}
-- main loop
function TIC()
player:update()
objsUpdate(bullets)
objsRemove(bullets)
cls(0)
objsDraw(bullets)
player:draw()
print("SCORE: "..score,2,2)
print("bullets: "..#bullets,120,2)
t=t+1
end
Bulletクラスの他にも、複数のオブジェクトを処理する関数、objsUpdate()、objsDraw()、objsRemove() を書いた。また、プレイヤーの更新処理(update())の中で、一定時間毎に弾を発生させる処理を追加した。
弾は、初期座標、速度、角度(方向)を与えて発生させている。弾は、その方向にまっすぐ進んで、画面外に出たら自分の存在フラグ(?)を false にする。後で、objsRemove() が、存在フラグが false になってるインスタンスを探して削除してくれる。
◎ 雑魚敵を出す。 :
雑魚敵を出してみる。Zakoクラスを作って、これまた一定時間毎に出してみる。とりあえず、弾と同様に、まっすぐ進むだけの処理を書いておいた。

さて、弾を撃って、雑魚敵も出せたから、アタリ判定を入れたくなるなと…。
とりあえず、今日はここまで。ブラウザ上で動く版は以下。
_stgsample3.tic.html

-- title: stg sample 3
-- author: mieki256
-- desc: short description
-- script: lua
scrw,scrh=240,136
-- --------------------
-- Player class
Player={}
Player.new=function()
local o={
alive=true,
x=scrw/2,
y=scrh/2,
sprid=5
}
setmetatable(o,{__index=Player})
return o
end
Player.update=function(self)
local spd=2.0
local dx,dy=0,0
if btn(0) then dy=-spd end
if btn(1) then dy=spd end
if btn(2) then dx=-spd end
if btn(3) then dx=spd end
if dx~=0 and dy~=0 then
local d=math.cos(math.rad(45))
dx=dx*d
dy=dy*d
end
self.x=self.x+dx
self.y=self.y+dy
self.x=math.min(math.max(self.x,8),scrw-8)
self.y=math.min(math.max(self.y,8),scrh-8)
-- shot
if t%6==0 then
local o
local x,y=self.x,self.y
spd=6
table.insert(bullets,Bullet.new(x,y,spd,0))
-- table.insert(bullets,Bullet.new(x,y,spd,12))
-- table.insert(bullets,Bullet.new(x,y,spd,-12))
-- table.insert(bullets,Bullet.new(x,y,spd,180))
end
end
Player.draw=function(self)
local x,y=self.x-8,self.y-8
spr(self.sprid,x,y,14,1,0,0,2,2)
end
-- --------------------
-- Bullet class
Bullet={}
Bullet.new=function(_x,_y,_spd,_ang)
local o={
alive=true,
x=_x,y=_y,spd=_spd,ang=_ang,
sprid=7
}
local ra=math.rad(_ang)
o.dx=_spd*math.cos(ra)
o.dy=_spd*math.sin(ra)
setmetatable(o,{__index=Bullet})
return o
end
Bullet.update=function(self)
self.x=self.x+self.dx
self.y=self.y+self.dy
if self.x<-16 or self.x>scrw+16 or
self.y<-16 or self.y>scrh+16 then
self.alive=false
end
end
Bullet.draw=function(self)
local x,y=self.x-4,self.y-4
spr(self.sprid,x,y,0,1,0,0,1,1)
end
-- --------------------
-- Zako enemy class
Zako={}
Zako.new=function(_x,_y,_spd,_ang)
local o={
alive=true,x=_x,y=_y,
spd=_spd,ang=_ang,
sprid=1,t=0
}
local ra=math.rad(_ang)
o.dx=_spd*math.cos(ra)
o.dy=_spd*math.sin(ra)
setmetatable(o,{__index=Zako})
return o
end
Zako.update=function(self)
self.x=self.x+self.dx
self.y=self.y+self.dy
self.t=self.t+1
if self.x<-32 or self.x>scrw+32 or
self.y<-32 or self.y>scrh+32 then
self.alive=false
end
end
Zako.draw=function(self)
local x,y=self.x-8,self.y-8
spr(self.sprid,x,y,14,1,0,0,2,2)
end
-- --------------------
function objsUpdate(objs)
for i,o in ipairs(objs) do
o:update()
end
end
function objsDraw(objs)
for i,o in ipairs(objs) do
o:draw()
end
end
function objsRemove(objs)
local l=#objs
for i=l,1,-1 do
if not objs[i].alive then
table.remove(objs,i)
end
end
end
function bornEnemys()
if t%50==0 then
for i=1,3 do
local o,spd,ang,x,y
spd=1.5
x=scrw+16
y=math.random(8,scrh-8)
ang=180+25*((y-(scrh/2))/(scrh/2))
o=Zako.new(x,y,spd,ang)
table.insert(enemys,o)
end
end
end
-- init
score=0
t=0
player=Player.new()
bullets={}
enemys={}
-- main loop
function TIC()
bornEnemys()
player:update()
objsUpdate(bullets)
objsUpdate(enemys)
objsRemove(bullets)
objsRemove(enemys)
cls(0)
objsDraw(enemys)
objsDraw(bullets)
player:draw()
print("SCORE: "..score,2,2)
print("enemys: "..#enemys,120,2)
t=t+1
end
さて、弾を撃って、雑魚敵も出せたから、アタリ判定を入れたくなるなと…。
とりあえず、今日はここまで。ブラウザ上で動く版は以下。
_stgsample3.tic.html
[ ツッコむ ]
以上、1 日分です。