mieki256's diary



2017/11/27(月) [n年前の日記]

#1 [love2d][lua] love2dでシューティングゲームの自機っぽいものを書いてたり

love2dを使って、縦スクロールシューティングゲームの自機っぽい処理を書いてたり。

こんな感じに。カーソルキーで移動。Zキーで弾を撃つ方向の変更/固定を切り替え。

stg_player_test01_ss.gif

動作確認環境は、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

ソース。 :

_conf.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")

以上です。

過去ログ表示

Prev - 2017/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