2017/11/27(月) [n年前の日記]
#1 [love2d][lua] love2dでシューティングゲームの自機っぽいものを書いてたり
love2dを使って、縦スクロールシューティングゲームの自機っぽい処理を書いてたり。
こんな感じに。カーソルキーで移動。Zキーで弾を撃つ方向の変更/固定を切り替え。
動作確認環境は、Windows10 x64 + love2d 0.10.2。
弾を加算合成で描画してるので、love2d 0.10.0以降が必要。もっとも、加算合成の指定部分を修正すれば love2d 0.9.x でも動きそうだけど…。
こんな感じに。カーソルキーで移動。Zキーで弾を撃つ方向の変更/固定を切り替え。
動作確認環境は、Windows10 x64 + love2d 0.10.2。
弾を加算合成で描画してるので、love2d 0.10.0以降が必要。もっとも、加算合成の指定部分を修正すれば love2d 0.9.x でも動きそうだけど…。
◎ 使用画像。 :
以下が自作の使用画像。License : CC0 / Public Domain ってことで。
_airplane_03_64x64_000.png
_airplane_03_64x64_001.png
_bg_640x480_02.png
_bullet_01_32x32.png
_airplane_03_64x64_000.png
_airplane_03_64x64_001.png
_bg_640x480_02.png
_bullet_01_32x32.png
◎ ソース。 :
_conf.lua
_main.lua
function love.conf(t) t.window.title = "Stg Player Test01" t.window.width = 1280 t.window.height = 720 t.window.vsync = true -- t.window.fullscreen = true -- t.window.fullscreentype = "exclusive" end
_main.lua
-- Stg Player Test01 angle_change_key = false function love.load() -- init -- set filter love.graphics.setDefaultFilter("nearest", "nearest") scr_w = 640 scr_h = 480 canvas = love.graphics.newCanvas(scr_w, scr_h) -- get window width and height wdw_w, wdw_h = love.graphics.getDimensions() scr_scale = math.min((wdw_w / scr_w), (wdw_h / scr_h)) scr_ofsx = (wdw_w - (scr_w * scr_scale)) / 2 scr_ofsy = (wdw_h - (scr_h * scr_scale)) / 2 -- load image bgimg = love.graphics.newImage("bg_640x480_02.png") player_img_a = love.graphics.newImage("airplane_03_64x64_000.png") player_img_b = love.graphics.newImage("airplane_03_64x64_001.png") bullet_img = love.graphics.newImage("bullet_01_32x32.png") -- define player class Player = {} Player.new = function(x, y, img_a, img_b) local obj = {} obj.activate = true obj.speed = 300 obj.x = x obj.y = y obj.gun_angle = -90 obj.gun_angle_change_mode = false obj.img_a = img_a obj.img_b = img_b obj.img_w = img_a:getWidth() obj.img_h = img_a:getHeight() setmetatable(obj, {__index = Player}) return obj end Player.update = function(self, dt) if not self.activate then return end if angle_change_key then self.gun_angle_change_mode = not(self.gun_angle_change_mode) angle_change_key = false end local ang = -1 if love.keyboard.isDown("left") then ang = 180 elseif love.keyboard.isDown("right") then ang = 0 end if love.keyboard.isDown("up") then if ang < 0 then ang = 270 elseif ang == 0 then ang = 270 + 45 else ang = 180 + 45 end elseif love.keyboard.isDown("down") then if ang < 0 then ang = 90 elseif ang == 0 then ang = 45 else ang = 180 - 45 end end if ang >= 0 then local spd = self.speed * dt local ra = math.rad(ang) self.x = self.x + (spd * math.cos(ra)) self.y = self.y + (spd * math.sin(ra)) if self.gun_angle_change_mode then -- gun angle change local tgt_ang = (ang + 180) % 360 if self.gun_angle ~= tgt_ang then local a = (tgt_ang - self.gun_angle) % 360 if a > 180 then a = a - 360 end local spd = 5 if a > 0 then if a < spd then self.gun_angle = tgt_ang else self.gun_angle = (self.gun_angle + spd) % 360 end elseif a < 0 then if a > -spd then self.gun_angle = tgt_ang else self.gun_angle = (self.gun_angle - spd) % 360 end end end end end -- move area check local wh = self.img_w / 2 local hh = self.img_h / 2 local xmin, ymin = wh, hh local xmax, ymax = scr_w - wh, scr_h - hh self.x = math.min(math.max(self.x, xmin), xmax) self.y = math.min(math.max(self.y, ymin), ymax) end Player.draw = function(self) if not self.activate then return end local ox, oy = self.img_w / 2, self.img_h / 2 local x, y = self.x, self.y local ang = math.rad(self.gun_angle) local scale = 1.0 love.graphics.draw(self.img_a, x, y, 0, scale, scale, ox, oy) love.graphics.draw(self.img_b, x, y, ang, scale, scale, ox, oy) end -- define player bullet class PlayerBullet = {} PlayerBullet.new = function(x, y, ang, img) local obj = { speed = 600, activate = true, x = x, y = y, ang = ang, img = img } obj.img_w, obj.img_h = img:getWidth(), img:getHeight() obj.img_wh, obj.img_hh = obj.img_w / 2, obj.img_h / 2 setmetatable(obj, {__index = PlayerBullet}) return obj end PlayerBullet.update = function(self, dt) if not self.activate then return end local ra = math.rad(self.ang) self.x = self.x + (math.cos(ra) * self.speed * dt) self.y = self.y + (math.sin(ra) * self.speed * dt) local xmin, ymin = -self.img_wh, -self.img_hh local xmax, ymax = scr_w + self.img_wh, scr_h + self.img_hh if self.x < xmin or self.x > xmax or self.y < ymin or self.y > ymax then self.activate = false end end PlayerBullet.draw = function(self) if not self.activate then return end love.graphics.draw(self.img, self.x, self.y, math.rad(self.ang), 1.0, 1.0, self.img_wh, self.img_hh) end -- work math.randomseed(0) player = Player.new(scr_w / 2, scr_h / 2, player_img_a, player_img_b) player_bullets = {} shot_timer = 0 -- framerate steady min_dt = 1 / 60 next_time = love.timer.getTime() end function love.update(dt) -- update next_time = next_time + min_dt if dt > 1.0 then return end player:update(dt) shot_timer = shot_timer + dt if shot_timer >= 0.1 then -- born player bullet shot_timer = shot_timer - 0.1 local x = player.x local y = player.y local ang = player.gun_angle + math.random(-20, 20) / 10 local obj = PlayerBullet.new(x, y, ang, bullet_img) table.insert(player_bullets, obj) end -- move player bullets for i, spr in ipairs(player_bullets) do spr:update(dt) end -- remove bullet local elen = #player_bullets for i=elen,1,-1 do if not player_bullets[i].activate then table.remove(player_bullets, i) end end end function love.draw() -- set canvas love.graphics.setCanvas(canvas) -- draw BG love.graphics.setColor(255, 255, 255) love.graphics.draw(bgimg, 0, 0) -- draw objects love.graphics.setColor(255, 255, 255) player:draw() love.graphics.setBlendMode("add") for i, spr in ipairs(player_bullets) do spr:draw() end love.graphics.setBlendMode("alpha") -- unset canvas love.graphics.setCanvas() -- draw canvas to window love.graphics.setColor(255, 255, 255) love.graphics.draw(canvas, scr_ofsx, scr_ofsy, 0, scr_scale, scr_scale) love.graphics.print("FPS: "..tostring(love.timer.getFPS()), 10, 10) love.graphics.print("env: "..tostring(love.system.getOS()), 10, 40) if love.system.getOS() == "Windows" then -- wait local cur_time = love.timer.getTime() if next_time <= cur_time then next_time = cur_time else love.timer.sleep(next_time - cur_time) end end end function love.keypressed(key, isrepeat) -- ESC to exit if key == "escape" then love.event.quit() elseif key == "z" then angle_change_key = true end end
◎ 少し説明。 :
加算合成描画は、love.graphics.setBlendMode("add") を指定してから描画すればいいらしい。通常描画に戻すときは、love.graphics.setBlendMode("alpha") を呼ぶ。
love.graphics.setBlendMode("add") for i, spr in ipairs(player_bullets) do spr:draw() end love.graphics.setBlendMode("alpha")
[ ツッコむ ]
#2 [cg_tools] シューティングゲームの自機に使えそうな画像
ゲームっぽいサンプルソースを書く際に自機の画像があったほうが雰囲気が出てモチベーションが上がる気がするので、自作画像を置いときます。License : CC0 / Public Domain ってことで。好きに使ってください。砲台部分は重ねて使うことを前提にして描いてある…けど無くてもいいような。
_airplane_02_64x64.png
_airplane_02_64x64_000.png
_airplane_02_64x64_001.png
_airplane_03_64x64.png
_airplane_03_64x64_000.png
_airplane_03_64x64_001.png
_airplane_04_64x64.png
_airplane_04_64x64_000.png
_airplane_04_64x64_001.png
_bullet_01_32x32.png
[ ツッコむ ]
#3 [cg_tools] 使い勝手の良いオープンな画像フォーマットは存在するのだろうか
ドット絵を作る際は、
_EDGE2
や
_GrafX2
を使って作業しているのだけど。GrafX2 で描いてgifで保存してから EDGE2 で開こうとしたら「アニメーションのデータがおかしいから開けんわい」と怒られてしまったり。
どうも GrafX2 は、レイヤーも含んだ状態で画像を保存するために、アニメGIFとして保存してる気配が。アニメGIFのフレームが、レイヤー扱いになってるのではないかなあ…。そしておそらく、アニメGIFではあり得ない形で情報を格納してるのかも。
こういう状態になるのも、レイヤーを含めることができて、かつ、仕様が公開されてるオープンな画像フォーマットが当時は存在してなかったから、なのだろうなと。
いやまあ、GIMP の .xcf とか一応あったけど、当時はフォーマット仕様が文書の形で存在してなくて「ソースが仕様だ」とか言ってたらしいし。Photoshop の .psd は仕様が公開されてなくて各自が解析しながら独自実装してたし。そもそもその手のCGソフトは、機能追加するたびに作業用ファイルに保存する情報も増えていくから、事前にカッチリと決めておくのが難しいという事情も。
しかし…。今なら何か使えそうなフォーマットがありそうな気も。
あるいは、今から新規に策定するなら、どういう仕様にするのが妥当なんだろう。各レイヤーは png にして、xmlで管理情報を保持、とかそんな感じだろうか…。
どうも GrafX2 は、レイヤーも含んだ状態で画像を保存するために、アニメGIFとして保存してる気配が。アニメGIFのフレームが、レイヤー扱いになってるのではないかなあ…。そしておそらく、アニメGIFではあり得ない形で情報を格納してるのかも。
こういう状態になるのも、レイヤーを含めることができて、かつ、仕様が公開されてるオープンな画像フォーマットが当時は存在してなかったから、なのだろうなと。
いやまあ、GIMP の .xcf とか一応あったけど、当時はフォーマット仕様が文書の形で存在してなくて「ソースが仕様だ」とか言ってたらしいし。Photoshop の .psd は仕様が公開されてなくて各自が解析しながら独自実装してたし。そもそもその手のCGソフトは、機能追加するたびに作業用ファイルに保存する情報も増えていくから、事前にカッチリと決めておくのが難しいという事情も。
しかし…。今なら何か使えそうなフォーマットがありそうな気も。
あるいは、今から新規に策定するなら、どういう仕様にするのが妥当なんだろう。各レイヤーは png にして、xmlで管理情報を保持、とかそんな感じだろうか…。
[ ツッコむ ]
以上、1 日分です。