LÖVEでの開発

画像






この投稿の目標は、 LÖVEフレームワークを使用して可能な限り単純な形式で開発の主要な段階を説明することです。例として、アタリオートマトン小惑星の古典的なゲームを使用します。



ポチェルチキコーナー



LÖVEとは何ですか?



LÖVEは2次元ゲームのフレームワークです。 これはエンジンではなく、LuaとSDL2の間のレイヤーにすぎず、きれいな構文、OpenGLを機能させるための最小限の追加ジェスチャー、およびすぐに面白いものをすぐに残せるライブラリ(Box2dなど)などの追加の優れた機能を備えています起こったことを掘り下げる場所。 しかし、さらに、LÖVEは最小限のギャグと鉄との低レベルの相互作用を特徴としているため、フレームワーク(自己学習/さらなる使用のため)で独自のエンジンを作成したり、おもちゃをすぐにハードコーディングしたりできます。



フレームワークのシンプルさにより、特定のエンジンテクノロジーの開発ではなく、プログラミングプロセスに専念するプログラマーではない人のために、簡単なプロトタイプやミニゲームを作成することができます。 私の実践では、二次方程式の根を計算したり、信用率を計算したりするための古典的な実験室での作業よりもはるかに多くの喜びを持って14-17歳の生徒がシンプルなゲームの開発に従事していることを示しています。優れたプログラマーになります。



なぜルア? この言語学習するのに十分単純で、JavaScriptやPythonよりも単純ですが、上記の言語低レベル(C / C ++)の両方に切り替えるのは非常に簡単です。 また、より大きなもの(cryEngine、GMod、MinecraftのOpenComputersなど)の一部として、ビデオゲームの開発でも非常に人気があり、一部のゲームに改造がある場合は、Luaを使用する可能性が非常に高くなります。

標準ライブラリの貧困は恐れることはありませんが、ほとんどのタスクにはサードパーティの開発があります(実際に言語標準になるほど人気が​​あります)が、開発の速度や言語インタープリターをマイクロコントローラーに押し込めるなど、貧困にもマイナス面があります。スクリプトの長所と短所をすべて備えています。



さらに、LÖVEにはデフォルトでLuaJIT仮想マシンが付属しており、これにより実行が何倍も高速化され(ゲームに不可欠)、FFIを使用できるようになります:Cで記述されたライブラリの接続、C構造の初期化と使用、メタテーブルを使用してluaオブジェクトに変換、そして、作成時間/メモリなどを節約します



ポイントに少し近づいた





さらに作業を進めるには、次の一連のアクションを実行する必要があります。

  1. 公式ウェブサイトからLÖVEの最新バージョンをダウンロードしてください
  2. LÖVEで現在のプロジェクトの起動を設定します。標準のテスト実行方法は、love実行可能ファイルのmain.luaファイルでディレクトリを開くことです。 ディレクトリの内容をmain.luaファイルとともにzipアーカイブにパックし、それを実行可能ファイルにドラッグするか、.zipの名前を.loveに変更してファイルの関連付けを構成することもできます。 現在のエディターのショートカットを構成する方が簡単だと思います。メモ帳++の場合、たとえば次のようになります。
    <Command name=...>path/to/love.exe $(CURRENT_DIRECTORY)</Command>
          
          



    崇高の例は、 次の記事で見つけることができます。
  3. 空のディレクトリを作成し、main.luaというファイルを追加します。 パスにスペースやキリル文字がないことが望ましいです。そうでなければ、スペースを詰め込んで文句を言う人もいますが、回避するには、ショートカットまたは起動方法をわずかに変更できます。
  4. お気に入りのエディターで、クリーンでクリーンなファイルmain.luaとLÖVE-Wikiをお気に入りのブラウザーで開きます。


さらに近いが、完全ではない



最初に知っておくべきことは、フレームワークが、すでに宣言されているグローバルloveテーブルに書き込むコールバックのセットを介して機能することです。



 function love.load(arg) --    love.load    , --     . end function love.update(dt) --   update  draw    , -- ,   : -- "->->->->" --       . end function love.draw() --       - --     love. love.graphics.print('Hello dear Love user!', 100, 100) end
      
      





このコードを実行した後、あなたは啓発されたと感じ、次のステップに進んでください:何か便利なものにリモートで似たもの。



すでにケースに似たもの



Luaにはデフォルトで「通常のOOP」がないため、この記事の3.2項の初心者向けの構造はかなり複雑になりますが、表に慣れていない場合は3番目の段落全体を読む必要があります。



まず、私たちは小惑星をやっているので、ボートを手に入れたいです。



次に、ヒットできるものとターゲットを撮影します。

同様に、ポイントを数え、どこかで連続してすべてを操作したいと思います。



さらに多くのコードがありますが、コメントが非常に有益であることを願っています。



 --      ,  , --       . local Ship, Bullet, Asteroid, Field Ship = {} --   ,    ship, --       ship. Ship.__index = Ship --       ,     Ship.type = 'ship' --  -        'self'. function Ship:new(field, x, y) -- ,   self,   Ship. --  self   , self   Ship   . self = setmetatable({}, self) --       ,   . self.field = field -- : self.x = x or 100 -- 100 -  self.y = y or 100 --   : self.angle = 0 --    : --  : self.vx = 0 self.vy = 0 -- , /: self.acceleration = 200 --  : self.rotation = math.pi --   : self.shoot_timer = 0 self.shoot_delay = 0.3 -- ,  : self.radius = 30 --   ,    : self.vertexes = {0, -30, 30, 30, 0, 20, -30, 30} --[[  - ,   : /\ / \ /_/\_\ ]] --   . return self end function Ship:update(dt) --  ,   ,     , ? -- dt -  ,      . self.shoot_timer = self.shoot_timer - dt -- : -- "     " -   . if love.keyboard.isDown('x') and self.shoot_timer < 0 then self.field:spawn(Bullet:new(self.field, self.x, self.y, self.angle)) --   ,         , --    . self.shoot_timer = self.shoot_delay end if love.keyboard.isDown('left') then --  ,   dt -   1, -- ,  ,     Pi, --   -  ,    . self.angle = self.angle - self.rotation * dt end if love.keyboard.isDown('right') then self.angle = self.angle + self.rotation * dt end if love.keyboard.isDown('up') then --   ,      . local vx_dt = math.cos(self.angle) * self.acceleration * dt local vy_dt = math.sin(self.angle) * self.acceleration * dt --      . self.vx = self.vx + vx_dt self.vy = self.vy + vy_dt end --         . self.x = self.x + self.vx * dt self.y = self.y + self.vy * dt --    ,       : --    ,   . --    - --   ,   . self.vx = self.vx - self.vx * dt self.vy = self.vy - self.vy * dt --      : --       , --       . local screen_width, screen_height = love.graphics.getDimensions() if self.x < 0 then self.x = self.x + screen_width end if self.y < 0 then self.y = self.y + screen_height end if self.x > screen_width then self.x = self.x - screen_width end if self.y > screen_height then self.y = self.y - screen_height end end function Ship:draw() --   , --        . love.graphics.setColor(255,255,255) --     , --  ,     . --     . love.graphics.push() --       . love.graphics.translate (self.x, self.y) --      . --  Pi/2 ,      --      , ,   --       . love.graphics.rotate (self.angle + math.pi/2) --   , line - , fill -  . love.graphics.polygon('line', self.vertexes) -- , ,      -- ( love.graphics.push()). love.graphics.pop() --    , --  /  : --     ,     --      /. --          . --       , end -- "!   ! ? ,   !" --    . --  ,   ,    . --        : Bullet = {} Bullet.__index = Bullet --  -      , --         , --      : Bullet.type = 'bullet' Bullet.speed = 300 function Bullet:new(field, x, y, angle) self = setmetatable({}, self) --    self.field = field self.x = x self.y = y self.radius = 3 --   self.life_time = 5 --     --       : self.vx = math.cos(angle) * self.speed self.vy = math.sin(angle) * self.speed --     self   speed, --        -- __index   return self end function Bullet:update(dt) --   : self.life_time = self.life_time - dt if self.life_time < 0 then --      , --    . self.field:destroy(self) return end --    self.x = self.x + self.vx * dt self.y = self.y + self.vy * dt --         local screen_width, screen_height = love.graphics.getDimensions() if self.x < 0 then self.x = self.x + screen_width end if self.y < 0 then self.y = self.y + screen_height end if self.x > screen_width then self.x = self.x - screen_width end if self.y > screen_height then self.y = self.y - screen_height end end function Bullet:draw() love.graphics.setColor(255,255,255) --    . -- , ,      love.graphics.circle('fill', self.x, self.y, self.radius) end --   ?   , . Asteroid = {} Asteroid.__index = Asteroid Asteroid.type = 'asteroid' function Asteroid:new(field, x, y, size) self = setmetatable({}, self) --   . --      , --        . self.field = field self.x = x self.y = y --     1-N. self.size = size or 3 --    -   . self.vx = math.random(-20, 20) self.vy = math.random(-20, 20) self.radius = size * 15 --   --    , --       --   .    . --   ,      : self.hp = size + math.random(2) --      . self.color = {math.random(255), math.random(255), math.random(255)} return self end --   ,     function Asteroid:applyDamage(dmg) --     -   dmg = dmg or 1 self.hp = self.hp - 1 if self.hp < 0 then --   -   self.field.score = self.field.score + self.size * 100 self.field:destroy(self) if self.size > 1 then --    . for i = 1, 1 + math.random(3) do self.field:spawn(Asteroid:new(self.field, self.x, self.y, self.size - 1)) end end --    ,  true,     . return true end end --         local function collide(x1, y1, r1, x2, y2, r2) --       : local distance = (x2 - x1) ^ 2 + (y2 - y1) ^ 2 --        -  . --          . local rdist = (r1 + r2) ^ 2 return distance < rdist end function Asteroid:update(dt) self.x = self.x + self.vx * dt self.y = self.y + self.vy * dt --          , --        : for object in pairs(self.field:getObjects()) do --      . if object.type == 'bullet' then if collide(self.x, self.y, self.radius, object.x, object.y, object.radius) then self.field:destroy(object) --    -  true. if self:applyDamage() then --     -    return end end elseif object.type == 'ship' then if collide(self.x, self.y, self.radius, object.x, object.y, object.radius) then --  messagebox   . --   ,     . local head = 'You loose!' local body = 'Score is: '..self.field.score..'\nRetry?' local keys = {"Yea!", "Noo!"} local key_pressed = love.window.showMessageBox(head, body, keys) --     "Noo!": if key_pressed == 2 then love.event.quit() end self.field:init() return end end end --   - ,    ! local screen_width, screen_height = love.graphics.getDimensions() if self.x < 0 then self.x = self.x + screen_width end if self.y < 0 then self.y = self.y + screen_height end if self.x > screen_width then self.x = self.x - screen_width end if self.y > screen_height then self.y = self.y - screen_height end end function Asteroid:draw() --    : love.graphics.setColor(self.color) -- , ,      love.graphics.circle('line', self.x, self.y, self.radius) end -- ,      : Field = {} Field.type = 'Field' --   ,       , --    __index  ,    , --      . --   /  -   . function Field:init() self.score = 0 --       self.objects = {} local ship = Ship:new(self, 100, 200) print(ship) self:spawn(ship) end function Field:spawn(object) --     : --        . self.objects[object] = object end function Field:destroy(object) --   . self.objects[object] = nil end function Field:getObjects() return self.objects end function Field:update(dt) --     ,    . --      . local asteroids_count = 0 for object in pairs(self.objects) do --     if object.update then object:update(dt) end if object.type == 'asteroid' then asteroids_count = asteroids_count + 1 end end if asteroids_count == 0 then for i = 1, 3 do --       local y = math.random(love.graphics.getHeight()) self:spawn(Asteroid:new(self, 0, y, 3)) end end end function Field:draw() for object in pairs(self.objects) do if object.draw then object:draw() end end love.graphics.print('\n Score: '..self.score) end --  :        : function love.load() Field:init() end function love.update(dt) Field:update(dt) end function love.draw() Field:draw() end
      
      





コピーして貼り付けて最初に上記のシートを実行すると、古典的な小惑星に似たものが得られます。



画像



それはよさそうですが、もっとうまくやることができます:



1.オブジェクトの計算を高速化する空間インデックス。

2.主要な識別子を使用した、マネージャーのより良い組織。

3.同様に、ゲームオブジェクトのクラスに継承を適用​​し、座標と半径などを持つ「真空中の球体」(文字通り)オブジェクトから継承します。



これらの項目の実装は、シートを発掘し、さらに深く掘り下げることを決定した人たちの宿題のままです。



はい、この資料はLÖVEバージョン0.10.2向けに作成されています。

バージョン0.11.X以前を見つける将来の人々の場合:このソースコードでは、値を範囲0-255から対応する比率0-1に変更することにより、カラーテーブルを修正する必要があります。 例えば:



  --   : color = {0, 127, 255} --   -  : color = {0, 0.5, 1}
      
      





PS:「小さなおもちゃやこのフレームワーク用ツールの作成に関する記事には価値があるでしょうか」というトピックに対するフィードバックと回答を喜んでいます。



All Articles