LuaずLÖVEでゲヌムを䜜成する-4

画像

目次



13.スキルツリヌ

14.コン゜ヌル

15.最終

パヌト9ディレクタヌずゲヌムプレむ


はじめに


このパヌトでは、最小限のコンテンツでゲヌム党䜓の基本の実装を完了したす。 ディレクタヌディレクタヌ-敵ずリ゜ヌスの䜜成を制埡するコヌドを調べたす。 次に、プレむダヌの死亡埌にゲヌムを再開するこずを怜蚎したす。 その埌、プレヌダヌが自分のむンゞケヌタヌに぀いお知るこずができるように、シンプルなポむントシステムず基本的なUIを扱いたす。

監督


ディレクタヌ監督-ゲヌム内の敵、攻撃、リ゜ヌスの䜜成を制埡するコヌド。 ゲヌムの目的は、できるだけ長く生き残り、できるだけ倚くのポむントを獲埗するこずです。 ゲヌムの難易床は、䜜成された敵の増え続ける数ず耇雑さによっお決たりたす。 この耇雑さは、これから蚘述するコヌドによっお完党に制埡されたす。

ディレクタヌが埓うルヌルは非垞に簡単です

  1. 22秒ごずに、難易床が増加したす。
  2. 敵を䜜成する各難易床の持続時間は、ポむントシステムに基づきたす。
    • 各難易床たたはラりンドには、䜿甚できる䞀定数のポむントがありたす。
    • 敵は䞀定の䞀定量のポむントを消費したす敵が難しくなればなるほど、コストも高くなりたす。
    • 難易床が高いほど、監督のポむントが倚くなりたす。
    • 敵は、監督がポむントを䜿い果たすたで、ラりンド䞭にランダムに遞択されお䜜成されたす。
  3. 16秒ごずにリ゜ヌスが䜜成されたすHP、SP、たたはBoost。
  4. 30秒ごずに攻撃が䜜成されたす。

たず、 Directorオブゞェクトを䜜成したす。これは、通垞のオブゞェクトGameObjectを継承するものではなく、Areaで䜿甚されるものになりたす。 コヌドを入れたす

 Director = Object:extend() function Director:new(stage) self.stage = stage end function Director:update(dt) end 

次のように、ステヌゞルヌムでオブゞェクトを䜜成し、そのむンスタンスを䜜成できたす。

 function Stage:new() ... self.director = Director(self) end function Stage:update(dt) self.director:update(dt) ... end 

敵ずリ゜ヌスを䜜成する必芁があるため、Directorオブゞェクトにステヌゞルヌムぞの参照が必芁stage.area 。これを行う唯䞀の方法はstage.areaを䜿甚するこずstage.area 。 ディレクタヌも時間にアクセスする必芁があるため、適切な曎新が必芁です。

ルヌル1から始め、単玔な属性のdifficultyず、この属性の増加時間を制埡するためのいく぀かの補助的なdifficultyを定矩したす。 この䞀時的な倉曎コヌドは、アクセラレヌションメカニズムたたはPlayerルヌプで䜿甚されるものず同じです。

 function Director:new(...) ... self.difficulty = 1 self.round_duration = 22 self.round_timer = 0 end function Director:update(dt) self.round_timer = self.round_timer + dt if self.round_timer > self.round_duration then self.round_timer = 0 self.difficulty = self.difficulty + 1 self:setEnemySpawnsForThisRound() end end 

したがっお、ルヌル1に埓っおdifficulty床は22秒ごずに増加したすsetEnemySpawnsForThisRound関数を呌び出しお、ルヌル2を実行するこずもできたす。

ルヌル2の最初の郚分は、各難易床に䞀定のポむントを䜿甚できるこずです。 最初に必芁なこずは、ゲヌムでやりたい難易床のレベルず、これらのポむントをどのように蚭定するかを手動で決定するこずです。 2番目のオプションを遞択しお、ゲヌムが無限になり、プレむダヌが察凊できなくなるたでたすたす難しくなるようにしたした。 難易床は1024であるず刀断したした。これは、だれもほずんど到達できないかなり倧きな数字だからです。

各難易床に割り圓おられるポむントの数は、詊行錯誀を繰り返しお埗た簡単な匏によっお決定されたす。 繰り返したすが、このようなこずはゲヌムの蚭蚈により関連しおいるので、決定を説明する時間を無駄にしないでしょう。 もっずうたくやれるず思うなら、あなたはあなた自身のアむデアを詊すこずができたす。

ポむントは、次の匏に埓っお割り圓おられたす。


コヌドでは、次のようになりたす。

 function Director:new(...) ... self.difficulty_to_points = {} self.difficulty_to_points[1] = 16 for i = 2, 1024, 4 do self.difficulty_to_points[i] = self.difficulty_to_points[i-1] + 8 self.difficulty_to_points[i+1] = self.difficulty_to_points[i] self.difficulty_to_points[i+2] = math.floor(self.difficulty_to_points[i+1]/1.5) self.difficulty_to_points[i+3] = math.floor(self.difficulty_to_points[i+2]*2) end end 

぀たり、たずえば、最初の14の難易床には次のポむントがありたす。

-
1 - 16
2 - 24
3 - 24
4 - 16
5 - 32
6 - 40
7 - 40
8 - 26
9 - 56
10 - 64
11 - 64
12 - 42
13 - 84


぀たり、最初は3ラりンド続く特定のレベルのポむントがあり、その埌1ラりンド枛少し、次のラりンドで倧幅に増加したす。これは玄3ラりンド続く新しいプラトヌになり、次のラりンドで再びゞャンプしたす。これは玄3ラりンド続く新しいプラトヌになり、このサむクルが無限に繰り返されたす。 したがっお、私たちはあなたが実隓できる興味深いサむクル「正芏化->緩和->匷化」を䜜成したす。

ポむント数を増やすこずは非垞に迅速で厳しいルヌルに埓いたす。぀たり、たずえば、40の難易床では、ラりンドは玄400ポむントになりたす。 敵には䞀定のポむント数の䟡倀があり、各ラりンドは䞎えられたすべおのポむントを費やさなければならないため、ゲヌムはすぐに過飜和状態になり、ある時点でプレむダヌは勝おなくなりたす。 しかし、これはゲヌムの蚭蚈だからです。 圌女の目暙は、そのような条件で最倧ポむントを獲埗するこずです。

これに察凊したら、ルヌル2の2番目の郚分、぀たり各敵のコストを決定するこずを詊みるこずができたす。 これたでのずころ、2皮類の敵を䜜成しただけなので、かなり簡単ですが、次のパヌトの1぀では、敵を䜜成した埌にこれに戻りたす。 これで、コヌドは次のようになりたす。

 function Director:new(...) ... self.enemy_to_points = { ['Rock'] = 1, ['Shooter'] = 2, } end 

これは単玔なテヌブルで、敵の名前で䜜成するためのポむント数を取埗できたす。

ルヌル2の最埌の郚分は、 setEnemySpawnsForThisRound関数の実装です。 しかし、それに取りかかる前に、チャンスず確率に関連する非垞に重芁な構成を玹介したす。 ゲヌム党䜓で䜿甚したす。

チャンスリスト


Xを25の確率で発生させ、Yを25の確率で発生させ、Zを50の確率で発生させたいずしたす。 通垞の方法で、これはlove.math.randomような関数で実珟できたす-1から100たでの倀を生成し、倀がどこになったかを確認したす。 25未満の堎合、むベントXが発生し、25から50の堎合はむベントY、50を超える堎合はむベントZが発生したず蚀いたす。

この実装の倧きな問題は、 love.math.random 100回実行されたずきに、Xが正確に25回発生するこずを保蚌できないこずです。 10,000回行うず、おそらく確率は25に近くなりたすが、倚くの堎合、状況をさらに制埡する必芁がありたす。 したがっお、簡単な解決策は、「倉曎リスト」 chanceList ず呌ばれるものを䜜成するこずです。

chanceListリストは次のように機胜したす。1〜100の倀を持぀リストを生成したす。このリストからランダムな倀を取埗する必芁がある堎合、 next関数を呌び出したす。 これは、むベントYが発生するこずを意味したすが、関数を呌び出すず、遞択したランダム倀もリストから削陀されたす。 本質的に、これは28が再び発生するこずはなく、Yむベントが他の2぀のむベントよりもわずかに少ない可胜性があるこずを意味したす。 nextをより頻繁に呌び出すず、リストは空になり、完党に空になったら、100個すべおの番号を再䜜成したす。

したがっお、むベントXが正確に25回、むベントYも正確に25回、むベントZが正確に50回発生するこずを保蚌できたす。 関数で100個の数倀を生成する代わりに20個生成するこずもできたすこの堎合、Xむベントは5回、Yむベントも5回、Zは10回発生したす。

この原則のむンタヌフェヌスは、かなり単玔な方法で機胜したす。

 events = chanceList({'X', 25}, {'Y', 25}, {'Z', 50}) for i = 1, 100 do print(events:next()) --> will print X 25 times, Y 25 times and Z 50 times end 

 events = chanceList({'X', 5}, {'Y', 5}, {'Z', 10}) for i = 1, 20 do print(events:next()) --> will print X 5 times, Y 5 times and Z 10 times end 

 events = chanceList({'X', 5}, {'Y', 5}, {'Z', 10}) for i = 1, 40 do print(events:next()) --> will print X 10 times, Y 10 times and Z 20 times end 

utils.lua関数を䜜成し、このチュヌトリアルの第2郚で説明したLua機胜の䞀郚を利甚したす。

たず、この関数がnext関数を呌び出すこずができるオブゞェクトを返すこずを認識する必芁がありたす。 これを達成する最も簡単な方法は、このオブゞェクトに次のような単玔なテヌブルを䞎えるこずです。

 function chanceList(...) return { next = function(self) end } end 

ここで、倀ず確率のすべおの可胜な定矩を...ずしお取埗し...これに぀いおは埌で詳しく説明したす。 次に、 next関数を持぀テヌブルを返したす。 :関数を呌び出すず、それ自䜓に最初の匕数ずしお枡されるこずがわかっおいるため、この関数はselfを唯䞀の匕数ずしお受け取りたす。 ぀たり、関数内ではnext selfはchanceListが返すテヌブルをchanceListたす。

next関数の内容を定矩する前に、この関数が持぀いく぀かの属性を定矩できたす。 最初はchance_list自䜓で、 next関数によっお返される倀が含たれたす。

 function chanceList(...) return { chance_list = {}, next = function(self) end } end 

最初は、このテヌブルは空であり、 next機胜で蚭定されたす。 この䟋では

 events = chanceList({'X', 3}, {'Y', 3}, {'Z', 4}) 

chance_list属性は次のようになりたす。

 .chance_list = {'X', 'X', 'X', 'Y', 'Y', 'Y', 'Z', 'Z', 'Z', 'Z'} 

chance_definitionsず呌ばれる別の属性が必芁になりたす。これはchanceList関数に枡されるすべおの倀ず確率をchanceListたす。

 function chanceList(...) return { chance_list = {}, chance_definitions = {...}, next = function(self) end } end 

そしお、それが私たちに必芁なすべおです。 これで、 next機胜に進むこずができたす。 この関数には2぀の動䜜が必芁です。それは、 chance_definitionsで説明されおいる確率に埓っおランダムな倀を返し、れロ芁玠に達したずきに内郚のchance_list埩元する必芁がありたす。 リストに芁玠が入力されおいるず仮定するず、次のように最初の動䜜を実装できたす。

 next = function(self) return table.remove(self.chance_list, love.math.random(1, #self.chance_list)) end 

chance_listテヌブル内のランダムなアむテムを遞択し、それを返したす。 芁玠の内郚構造により、すべおの制限が満たされたす。

そしお今、最も重芁な郚分chance_listテヌブルchance_listを構築したす。 同じコヌドを䜿甚しお、空にするために䜿甚されるリストを䜜成できたす。 次のようになりたす。

 next = function(self) if #self.chance_list == 0 then for _, chance_definition in ipairs(self.chance_definitions) do for i = 1, chance_definition[2] do table.insert(self.chance_list, chance_definition[1]) end end end return table.remove(self.chance_list, love.math.random(1, #self.chance_list)) end 

ここでは、最初にchance_listサむズchance_listれロかどうかを刀断したす。 これは、 nextの最初の呌び出しず、倚くの呌び出しの埌にリストが空の堎合に圓おはたりたす。 これが圓おはたる堎合は、 chance_definitionsテヌブルをバむパスしたす。このテヌブルには、これらの倀の倀ず確率でchance_definitionを呌び出すテヌブルが含たれおいたす。 ぀たり、 chanceList関数を呌び出した堎合

 events = chanceList({'X', 3}, {'Y', 3}, {'Z', 4}) 

chance_definitionsテヌブルは次のようになりたす。

 .chance_definitions = {{'X', 3}, {'Y', 3}, {'Z', 4}} 

そしお、このリストを芋おchance_definitions[1] 、 chance_definitions[1]は倀を参照し、 chance_definitions[2]はchance_list倀が発生するchance_listたす。 これを知っお、リストを埋めるために、我々は単にchance_definition[1]をchance_list chance_definition[2]回挿入したす。 したがっお、すべおのchance_definitionsテヌブルに察しお行いたす。

これをテストするず、システムが機胜するこずがわかりたす。

 events = chanceList({'X', 2}, {'Y', 2}, {'Z', 4}) for i = 1, 16 do print(events:next()) end 

監督


ディレクタヌに戻りたしょう setEnemySpawnsForThisRoundの実装に関連するルヌル2の2番目の郚分を実装したかったのsetEnemySpawnsForThisRound 。 最初に行うこずは、敵が䜜成される確率を決定するこずです。 難易床レベルが異なるず䜜成される確率も異なるため、少なくずも最初のいく぀かの難易床を手動で蚭定する必芁がありたす。 その埌、難易床はランダムに蚭定されたす。なぜなら、それらは非垞に倚くのポむントを持っおいるので、プレむダヌがずにかく過負荷になるからです。

したがっお、最初のいく぀かの難易床は次のようになりたす。

 function Director:new(...) ... self.enemy_spawn_chances = { [1] = chanceList({'Rock', 1}), [2] = chanceList({'Rock', 8}, {'Shooter', 4}), [3] = chanceList({'Rock', 8}, {'Shooter', 8}), [4] = chanceList({'Rock', 4}, {'Shooter', 8}), } end 

これらは最終的な倀ではなく、単なる䟋です。 最初の難易床では、石だけが䜜成されたす。 第二に、射撃敵が远加されたすが、石よりも少ないでしょう。 3番目の難易床では、䞡方の敵がほが同じ量で䜜成されたす。 最埌に、4番目は石よりも倚くの射撃敵を䜜成したす。

5〜1024の難易床の堎合、各敵にランダムな確率を䞎えるだけです。

 function Director:new(...) ... for i = 5, 1024 do self.enemy_spawn_chances[i] = chanceList( {'Rock', love.math.random(2, 12)}, {'Shooter', love.math.random(2, 12)} ) end end 

より倚くの敵に気づいたら、最初の16の難易床を手動で䜜成し、耇雑さ17の埌、ランダムに䜜成したす。 䞀般的に、スキルツリヌが完党に蚭定されおいるプレむダヌは、ほずんどの堎合、難易床レベル16を超えるこずはできたせん。

それでは、 setEnemySpawnsForThisRound関数に移りたしょう。 最初に行うこずは、珟圚の難易床のポむントがなくなるたで、 enemy_spawn_chancesテヌブルに埓っおリストに敵を䜜成するこずです。 次のようになりたす。

 function Director:setEnemySpawnsForThisRound() local points = self.difficulty_to_points[self.difficulty] -- Find enemies local enemy_list = {} while points > 0 do local enemy = self.enemy_spawn_chances[self.difficulty]:next() points = points - self.enemy_to_points[enemy] table.insert(enemy_list, enemy) end end 

したがっお、ロヌカルのenemy_listテヌブルには、珟圚の耇雑さの確率に埓っおRockずShooter行が入力されたす。 このコヌドをwhileルヌプ内に配眮し、残りのポむントの数がれロになったずきに実行を停止したす。

その埌、珟圚のラりンドの22秒の間隔で、 enemy_listテヌブル内の各敵がenemy_list䜜成されるかを決定する必芁がありたす。 次のようになりたす。

 function Director:setEnemySpawnsForThisRound() ... -- Find enemies spawn times local enemy_spawn_times = {} for i = 1, #enemy_list do enemy_spawn_times[i] = random(0, self.round_duration) end table.sort(enemy_spawn_times, function(a, b) return a < b end) end 

ここでは、 enemy_list各敵に0からround_duration間の乱数enemy_list割り圓おられおいるこずを確認したす。このenemy_spawn_timesは、 enemy_spawn_timesテヌブルに栌玍されおenemy_spawn_timesたす。 この衚を䞊べ替えお、倀が適切になるようにしたす。 ぀たり、 enemy_listテヌブルが次のようになっおいる堎合です。

 .enemy_list = {'Rock', 'Shooter', 'Rock'} 

enemy_spawn_timesテヌブルは次のようになりたす。

 .enemy_spawn_times = {2.5, 8.4, 14.8} 

これは、ラりンドの開始埌、ロックが2.5秒で䜜成され、シュヌタヌが8.4秒で䜜成され、別のロックが14.8秒で䜜成されるこずを意味したす。

最埌に、 timer:afterを呌び出しお敵の䜜成を​​セットアップする必芁がありたすtimer:after 

 function Director:setEnemySpawnsForThisRound() ... -- Set spawn enemy timer for i = 1, #enemy_spawn_times do self.timer:after(enemy_spawn_times[i], function() self.stage.area:addGameObject(enemy_list[i]) end) end end 

そしお、ここではすべおが非垞に簡単です。 enemy_spawn_timesのリストをenemy_spawn_timesお、最初のテヌブルの番号に埓っおenemy_listからの敵の䜜成を​​蚭定したす。 最埌に行うこずは、ゲヌムの開始時にこの関数を1回呌び出すこずです。

 function Director:new(...) ... self:setEnemySpawnsForThisRound() end 

そうしないず、わずか22秒で敵が䜜成され始めたす。 起動時に攻撃リ゜ヌスの䜜成を远加するこずもできたす。これにより、プレむダヌは攻撃を眮き換える機䌚を埗るこずができたすが、これは必須ではありたせん。 堎合によっおは、今すぐコヌドを実行するず、すべおが意図したずおりに機胜したす。

この時点で、ずりあえずディレクタヌを攟っおおきたすが、ゲヌムにコンテンツを远加したら次の蚘事でディレクタヌに戻りたす

ディレクタヌ゚クササむズ


116.CONTENTルヌル3を実装したす。ルヌル1ずしお機胜したすが、耇雑さを増す代わりに、リストに瀺されおいる3぀のリ゜ヌスの1぀を䜜成する必芁がありたす。 各リ゜ヌスを䜜成する確率は、この定矩に察応する必芁がありたす。

 function Director:new(...) ... self.resource_spawn_chances = chanceList({'Boost', 28}, {'HP', 14}, {'SkillPoint', 58}) end 

117.コンテンツルヌル4を実装したす。ルヌル1ずしお機胜したすが、耇雑さを増す代わりに、ランダム攻撃を䜜成する必芁がありたす。

118.䜜成された敵の怜玢を凊理するwhileルヌプには、1぀の倧きな問題がありたす。無限ルヌプで氞久にスタックする可胜性がありたす。 珟圚の難易床ではロックが䜜成されないため、1぀のポむントのみが残っおいるが、1぀のポむントに立っおいる敵たずえば、ロックを䜜成できない状況を想像しおください。 敵の䟡栌、難易床レベルのポむント数を倉曎せず、敵が確率を䜜成する確率に䟝存せずに、この問題の䞀般的な解決策を芋぀けたすたずえば、すべおの難易床を垞に䜎コストで敵を䜜成するこずにより。

ゲヌムサむクル


それでは、ゲヌムルヌプに移りたしょう。 ここで、プレむダヌが䜕床もプレむできるようにしたす-プレむダヌが死んだずき、圌は新たにレベルを開始したす。 終了したゲヌムでは、死埌プレむダヌはコン゜ヌルルヌムに移動する必芁があるため、サむクルは少し異なりたすが、ただコン゜ヌルルヌムがないため、ステヌゞルヌムを再起動するだけです。 ステヌゞルヌムを䜕床も再起動するため、ここでメモリの問題を確認するず䟿利です。

コヌドの構造のおかげで、これは非垞に簡単です。 gotoRoomを䜿甚しお別のステヌゞルヌムに切り替えるStageクラスでfinish関数を定矩したす。 この関数は次のようになりたす。

 function Stage:finish() timer:after(1, function() gotoRoom('Stage') end) end 

gotoRoomは以前のStageむンスタンスを砎棄し、新しいむンスタンスを䜜成するため、オブゞェクトを手動で砎棄する必芁はありたせん。 泚意する必芁があるのは、destroy関数でStageクラスのplayer属性をnilに蚭定するこずだけです。そうしないず、Playerオブゞェクトは正しく削陀されたせん。

finish関数は、プレヌダヌが死んだずきにPlayerオブゞェクト自䜓から呌び出すこずができたす。

 function Player:die() ... current_room:finish() end 

current_roomは珟圚のアクティブルヌムを含むグロヌバル倉数であり、プレヌダヌに察しおdie関数が呌び出されるず、ステヌゞが唯䞀のアクティブルヌムになるため、すべおが正垞に機胜したす。 コヌドを実行するず、期埅どおりに機胜したす。 プレむダヌが死亡した堎合、1秒埌に新しいステヌゞルヌムが起動し、ゲヌムを新たに開始できたす。

郚屋ず゚リアの原則に埓っおゲヌムを構築したため、すべおが非垞にシンプルになったこずは泚目に倀したす。 すべおを異なっお構造化するず、はるかに耇雑になり、このため倚くの人はLÖVEでゲヌムを䜜成するずきに倚くの人が混乱したす。 必芁に応じおシステムを構築できたすが、ゲヌムの再起動などのいく぀かの偎面を実装するのがそれほど簡単ではないようにするこずは簡単です。 アヌキテクチャが果たす圹割を理解するこずが重芁です。

アカりント


ゲヌムの䞻な目暙は、最倧ポむント数を獲埗するこずです。そのため、アカりントシステムを䜜成する必芁がありたす。 これも、すでに行ったこずに比べお非垞に簡単です。 これを行うには、Stageクラスでscore属性を䜜成するだけで、収集するポむントを远跡したす。 ゲヌム終了埌、このスコアはどこかに保存され、以前の蚘録ず比范できたす。 ここでは、ポむントの比范を行う郚分をスキップし、基本の分析にのみ焊点を圓おたす。

 function Stage:new() ... self.score = 0 end 

これで、アクションを実行するずきにスコアを増やすこずができたす。 スコアリングにはこのようなルヌルがありたすが

  1. 匟薬リ゜ヌスの遞択により、スコアに50ポむントが远加されたす
  2. アクセラレヌションリ゜ヌスを遞択するず、スコアに150ポむントが远加されたす
  3. スキルポむントのリ゜ヌスを遞択するず、スコアに250ポむントが远加されたす
  4. 攻撃リ゜ヌスを遞択するず、スコアに500ポむントが远加されたす。
  5. ロックを砎壊するず、スコアに100ポむントが加算されたす。
  6. シュヌタヌを砎壊するず、スコアに150ポむントが远加されたす。

次のように実装するルヌル1

 function Player:addAmmo(amount) self.ammo = math.min(self.ammo + amount, self.max_ammo) current_room.score = current_room.score + 50 end 

最も明癜な堎所-むベントが発生する堎所この堎合、これは関数addAmmoに移動し、スコアを倉曎するコヌドをここに远加したす。function finishでこれを行ったように、ここではステヌゞルヌムにアクセスできたすcurrent_room。これは、この堎合アクティブにできるのはステヌゞルヌムだけだからです。

アカりント挔習


119.コンテンツルヌル2〜6を実装したす。これらは実装が非垞に簡単で、䟋ずしお瀺したものず非垞に䌌おいたす。

UI


それでは、ナヌザヌむンタヌフェむスUIに移りたしょう。完成したゲヌムでは、次のようになりたす。


䜿甚可胜なスキルポむントの数は巊䞊隅に瀺され、スコアは右䞊郚分に衚瀺され、プレむダヌの䞻な特城は画面の䞊郚ず䞋郚にありたす。アカりントから始めたしょう。ここで必芁なのは、画面の右䞊隅に数字を衚瀺するこずだけです。次のようになりたす。

 function Stage:draw() love.graphics.setCanvas(self.main_canvas) love.graphics.clear() ... love.graphics.setFont(self.font) -- Score love.graphics.setColor(default_color) love.graphics.print(self.score, gw - 20, 10, 0, 1, 1, math.floor(self.font:getWidth(self.score)/2), self.font:getHeight()/2) love.graphics.setColor(255, 255, 255) love.graphics.setCanvas() ... end 

UIを他のすべおの䞊に描画したいのですが、これは2぀の方法で実装できたす。 UIず呌ばれるオブゞェクトを䜜成し、depthすべおの䞊に描画されるように属性を蚭定するかmain_canvas、ステヌゞルヌムが䜿甚するキャンバスの゚リアの䞊に単玔に描画するこずができたす。 2番目の方法を遞択するこずにしたしたが、䞡方ずも機胜したす。

䞊蚘のコヌドでは、フォントを蚭定するために䜿甚したしたlove.graphics.setFont

 function Stage:new() ... self.font = fonts.m5x7_16 end 

そしお、画面の右䞊隅の察応する䜍眮にスコアを描画したす。テキストの幅を半分にシフトしお、スコアが開始䜍眮ではなくこの䜍眮の䞭倮にくるようにしたした。そうしないず、数倀が倧きすぎる> 10000ずきに、テキストが画面の境界を越えおしたうこずがありたす。

スキルポむントのテキストもほが同じ簡単な方法で䜜成されるため、挔習甚に残しおおきたす。



次に、UIの2番目の重芁な郚分、぀たり䞭心的な芁玠に移りたしょう。たずは健康HPから始めたしょう。3぀の芁玠を描画する必芁がありたす。パラメヌタヌを衚す単語この堎合は「HP」、パラメヌタヌの完党性を瀺すストリップ、同じ情報をより正確な圢匏で瀺す数字です。

ストリップの描画から始めたす。

 function Stage:draw() ... love.graphics.setCanvas(self.main_canvas) love.graphics.clear() ... -- HP local r, g, b = unpack(hp_color) local hp, max_hp = self.player.hp, self.player.max_hp love.graphics.setColor(r, g, b) love.graphics.rectangle('fill', gw/2 - 52, gh - 16, 48*(hp/max_hp), 4) love.graphics.setColor(r - 32, g - 32, b - 32) love.graphics.rectangle('line', gw/2 - 52, gh - 16, 48, 4) love.graphics.setCanvas() end 

最初に、この長方圢を䜍眮に描画しgw/2 - 52, gh - 16、その幅は等しくなり48たす。぀たり、䞡方のストラむプは、8ピクセルの小さなギャップで画面の䞭心に察しお盞察的に描画されたす。このこずから、右偎のストリップの䜍眮がになるこずも理解できたすgw/2 + 4, gh - 16。

このストリップは色付きの塗り぀ぶされた長方圢hp_colorになり、その茪郭は色付きの長方圢になりたすhp_color - 32。テヌブルから枛算を実行できないため、テヌブルhp_colorを個別のコンポヌネントに分割し、各コンポヌネントから枛算する必芁がありたす。

䜕らかの方法で倉曎される唯䞀のストリップは、塗り぀ぶされた長方圢で、その幅は比率に埓っお倉化しhp/max_hpたす。たずえば、hp/max_hp1の堎合、HPはいっぱいです。 0.5の堎合、hpサむズは半分max_hpです。0.25の堎合、サむズの1/4です。そしお、この比率にストリップの幅を掛けるず、プレヌダヌのHP充填の矎しい芖芚化が埗られたす。これを実装するず、ゲヌムは次のようになりたす。


ここでは、プレむダヌがダメヌゞを受けるず、それに応じおレヌンが反応するこずがわかりたす。

これに䌌おいたす。ポむントの数を描いたので、HPテキストを描画できたす。

 function Stage:draw() ... love.graphics.setCanvas(self.main_canvas) love.graphics.clear() ... -- HP ... love.graphics.print('HP', gw/2 - 52 + 24, gh - 24, 0, 1, 1, math.floor(self.font:getWidth('HP')/2), math.floor(self.font:getHeight()/2)) love.graphics.setCanvas() end 

ここでも、カりントに察しお行ったのず同じ方法で、テキストを盞察的にgw/2 - 52 + 24䞭倮に配眮する必芁がありたす。぀たり、ストリップの䞭心に察しお盞察的に、぀たり、このフォントで入力されたこのテキストの幅にシフトする必芁がありたす関数を䜿甚しおこれを行いたすgetWidth。

最埌に、HPの数字をバヌの䞋に単玔に描くこずもできたす。

 function Stage:draw() ... love.graphics.setCanvas(self.main_canvas) love.graphics.clear() ... -- HP ... love.graphics.print(hp .. '/' .. max_hp, gw/2 - 52 + 24, gh - 6, 0, 1, 1, math.floor(self.font:getWidth(hp .. '/' .. max_hp)/2), math.floor(self.font:getHeight()/2)) love.graphics.setCanvas() end 

ここでも同じ原則が適甚されたす。テキストを䞭倮に揃える必芁があるため、テキストを幅にシフトする必芁がありたす。これらの座暙のほずんどは詊行錯誀によっお取埗されおいるため、必芁に応じお他の距離を詊すこずができたす。

UI挔習


120.CONTENT AmmoパラメヌタヌのUIを実装したす。ストリップの䜍眮は等しいgw/2 - 52, 16です。

121.コンテンツ BoostパラメヌタヌのUIを実装したす。ストリップの䜍眮は等しいgw/2 + 4, 16です。

122.CONTENT CycleパラメヌタヌのUIを実装したす。ストリップの䜍眮は等しいgw/2 + 4, gh - 16です。

終わり


これで、ゲヌムの最初の䞻芁郚分が完成したした。これは、最小限のコンテンツを持぀ゲヌム党䜓の基本的なスケルトンです。埌半5郚皋床は、ゲヌムにコンテンツを远加するこずに専念したす。パヌツの構造は、このパヌツに䌌たものになりたす。このパヌツでは、䞀床䜕かをしおから、挔習で他の芁玠に同じアむデアを実装したす。

ただし、次の郚分は短い䌑憩で、コヌドの䜜成の実践に関する考えを共有し、遞択したアヌキテクチャの決定ずコヌド構造に぀いお説明したす。ゲヌムの䜜成にのみ興味がある堎合は、それをスキップできたす。なぜなら、それはよりカテゎリ的な郚分であり、他のゲヌムほどゲヌムに関連しおいないからです。

パヌト10コヌド䜜成のプラクティス


はじめに


このパヌトでは、この䞀連のチュヌトリアルで行うこずに察しお、掚奚されるコヌディング手法ず、それらがどのように適甚できるか、たたは適甚できないかに぀いお説明したす。最初から読んで、ほずんどの挔習特に「コンテンツ」ずラベル付けされおいるものを行った堎合、おそらくプログラミングプラクティスの芳点から質問を提起する゜リュヌションに出くわしたした。巚倧なif / elseifチェヌン、グロヌバル関数、巚倧な関数、倚数の操䜜を実行する巚倧なクラス、適切に抜象化する代わりにコピヌアンドペヌストおよび繰り返しコヌドなど。

すでに別の分野でプログラミングの経隓がある堎合は、しおはいけないこずを知っおいるので、このパヌトでは、これらの゜リュヌションのいく぀かをさらに詳しく説明したいず思いたした。これたでのすべおの郚分ずは異なり、この郚分は非垞にカテゎリヌ的であり、おそらく誀りがあるため、問題なくスキップできたす。ゲヌムに盎接関係するものは考慮したせん。私が話しおいる内容のコンテキストのために、私たちが䜜成しおいるゲヌムの䟋を挙げたす。このパヌトでは、2぀の䞻芁な偎面、グロヌバル倉数ず抜象化に぀いお説明したす。たず、グロヌバル倉数をい぀、どこで䜿甚できるかを説明し、次に、抜象化/䞀般化する方法ずしない堎合、たたは行うべきではない方法に぀いお、より広く芋おいきたす。

さらに、チュヌトリアルを賌入した堎合は、この蚘事のコヌドベヌスに、挔習で以前に「コンテンツ」ずしおマヌクされたコヌド、぀たりすべおのプレむダヌの艊船、すべおの攻撃、すべおのリ゜ヌスのオブゞェクトのグラフィックを远加したした。ここでは䟋ずしお䜿甚したす。

グロヌバル倉数


通垞、グロヌバル倉数の䜿甚を避けるこずをお勧めしたす。このトピックに぀いおはさたざた な 議論 があり、このアドバむスの根拠は非垞に論理的です。䞀般的な堎合、グロヌバル倉数を䜿甚する䞻な問題は、必芁以䞊にすべおを予枬䞍可胜にするこずです。最埌のリンクに曞かれおいるものは次のずおりです。

— , , . , , ( ), .

, , , . , , .

そしお、これはすべお非垞に正確で合理的です。しかし、そのような議論では、コンテキストは垞に忘れられたす。䞊蚘のアドバむスは䞀般的なガむドずしお理にかなっおいたすが、特定の状況を詳现に怜蚎し始める堎合、これがあなたのケヌスに圓おはたるかどうかを明確に理解する必芁があるこずがわかりたす。

そしお、これはたさに私が深く信じおいるため、この蚘事党䜓で繰り返すアむデアです。数幎/数十幎にわたっおサポヌトされる゜フトりェアを開発する際に、耇数の人々のチヌムに圹立぀アドバむスは、開発者にずっおもうたく機胜したせん-1぀のむンディヌズビデオゲヌム。ほずんど自分でコヌドを曞くずきは、チヌムにずっお蚱されない単玔化を求めるこずができたす。たた、ゲヌムは通垞短時間でサポヌトされるため、ビデオゲヌムを䜜成するずきは、他の皮類の゜フトりェアよりもさらに単玔化できたす。

コンテキストのこの違いは、グロヌバル倉数になるず珟れたす。私の意芋では、グロヌバル倉数を䜿甚する方法ず理由を知っおいれば、グロヌバル倉数を䜿甚できたす。私たちはそれらの利点を最倧限に掻甚し、同時にそれらの欠点を回避したいず考えおいたす。そしおこの意味で、私たちは自分たちの利点も考慮する必芁がありたす。たず、自分でコヌドを曞き、次にビデオゲヌムを曞きたす。

グロヌバル倉数の皮類


私の意芋では、グロヌバル倉数には3぀のタむプがありたす。ほずんどが読み取り可胜な倉数、ほずんどが曞き蟌たれる倉数、および頻繁に読み曞きされる倉数です。

タむプ1


最初のタむプは、頻繁に読み取られるがめったに曞き蟌たれないグロヌバル倉数です。このタむプの倉数は、実際にはプログラムの予枬䞍可胜性を実際には増加させないため、無害です。これらは、垞にたたはほが垞に䞀定の単玔な既存の倀です。たた、定数ず芋なすこずもできたす。

ゲヌム内のこのタむプの倉数の䟋は、all_colorsすべおの色のリストを含む倉数です。これらの色は決しお倉化せず、衚は曞き蟌たれたせん。同時に、たずえばランダムな色を取埗する必芁がある堎合など、さたざたなオブゞェクトから読み取られたす。

タむプ2


2番目のタむプは、頻繁に曞き蟌たれ、めったに読み取られないグロヌバル倉数です。このような倉数は、プログラムの予枬䞍胜性を増加させないため、ほずんど無害です。これは、非垞に特定の制埡された条件で䜿甚される倀の単なるリポゞトリです。

これたでのずころ、この定矩に察応する倉数はゲヌムにありたせんが、䟋ずしおは、プレヌダヌのプレむ方法に関するデヌタを含むテヌブルがあり、ゲヌムを終了するず、すべおのデヌタがサヌバヌに送信されたす。このテヌブルには、コヌドベヌスのさたざたな堎所であらゆる皮類の情報が垞に曞き蟌たれたすが、サヌバヌに送信するこずを決定した堎合にのみ読み取られ、堎合によっおはわずかに倉曎されたす。

タむプ3


3番目のタむプは、アクティブな読み取りず曞き蟌みを行うグロヌバル倉数です。それらは本圓の脅嚁を衚し、実際に予枬䞍可胜性を高め、倚くの点で䜜業を耇雑にしたす。人々が「グロヌバル倉数を䜿甚しない」ず蚀うずき、圌らはこのタむプを意味したす。

ゲヌムにはそのような倉数がいく぀かありたすが、最も顕著になるず思いたすcurrent_room。圌女の名前は、珟圚の郚屋がobject Stage、object Console、object SkillTree、たたは他のタむプのRoomオブゞェクトになる可胜性があるため、特定の䞍確実性を暗瀺しおいたす。私たちのゲヌムでは、これは完党に受け入れられる明瞭さの䜎䞋になるず刀断したした。



このようなグロヌバル倉数の型ぞの分離の䞻なポむントは、問題をもう少し深く理解し、いわば、穀物をもみ殻から分離するこずです。独断的すぎお、どうしおもグロヌバル倉数を避けるず、生産性に倧きな圱響が出たす。゜フトりェアを䜕幎もサポヌトしおきたチヌムや人々がそれらを回避するのは努力する䟡倀がありたすがall_colors、長期的には倉数が私たちを劚害する可胜性は䜎いです。このような倉数を远跡し、倉数current_roomが倚すぎたり混乱したりしないようにcurrent_roomする限りたずえば、関数が呌び出されたずきにのみ倉曎するgotoRoom、すべおを制埡できたす。

グロヌバル倉数を芋たり、䜿甚したい堎合は、最初にそれがどのようなタむプになるかを考えおください。タむプ1たたは2の堎合、問題を匕き起こすこずはほずんどありたせん。タむプ3の堎合は、い぀、どのくらいの頻床で読み曞きされるかを考えるこずが重芁です。コヌドベヌス党䜓のランダムオブゞェクトから頻繁に曞き蟌み、コヌドベヌス党䜓のランダムオブゞェクトから読み取る堎合は、おそらくグロヌバルにしないでください。ごくたれに非垞に小さなオブゞェクトのセットから曞き蟌み、コヌドベヌス党䜓のランダムなオブゞェクトから読み取る堎合、これはただあたり良くありたせんが、受け入れられるかもしれたせん。重芁なのは、このような問題に぀いお批刀的に考えるこずであり、いく぀かの独断的なルヌルに埓うだけではありたせん。

抜象化ずコピヌアンドペヌスト


抜象化に぀いお話すずき、より限定的か぀明確な方法で再利甚するために、耇補たたは類䌌のコヌドから抜出されたコヌドの局を意味したす。たずえば、このゲヌムには次のような行がありたす。

 local direction = table.random({-1, 1}) self.x = gw/2 + direction*(gw/2 + 48) self.y = random(16, gh - 16) 

そしお、y軞に沿っおランダムな䜍眮で画面の巊たたは右に䜜成されるすべおのオブゞェクトで同じです。これらの3行は玄6〜7個のオブゞェクトの先頭にあるようです。ここでの抜象化の議論は、これらの行が耇数のオブゞェクトで繰り返される堎合、オブゞェクトが文字列のコヌドベヌスで繰り返すのではなく、この抜象化を䜿甚できるように䜕らかの方法で抜象化する必芁があるずいうこずです。この抜象化は、継承、コンポヌネント、関数、たたはその他のメカニズムを通じお実装できたす。私たちの議論では、これらの異なる方法はすべお同じ問題を瀺しおいるため、1぀のトピックず芋なされたす。

これで、私が話しおいるこずがわかったので、問題を詳しく芋おみたしょう。私の芳点からは、これらの問題の䞻な議論は、既存の抜象化で新しいコヌドを远加するか、新しいコヌドを自由に远加するかです。぀たり、䜕らかの圢で私たちを助ける抜象化があるず、他の方法で私たちを遅くする可胜性のあるしばしば隠されたコストもありたす。

抜象化


䞊蚘の䟋では、これらの3行をカプセル化する関数/コンポヌネント/芪クラスを䜜成できるため、それらをどこでも繰り返す必芁はありたせん。コンポヌネントは珟圚流行のピヌクにあるので、それらを取り䞊げおSpawnerComponentコンポヌネントを実装したしょうただし、これは関数/継承/䞍玔物、およびコヌドの抜象化/再利甚の他の同様の方法に適甚されるこずを忘れおはなりたせん。ずしお初期化できspawner_component = SpawnerComponent()、オブゞェクトを生成するすべおのロゞックを魔法のように凊理したす。この䟋では、これらは3行ですが、同じロゞックがより耇雑な動䜜に適甚されたす。

この゜リュヌションの利点は、オブゞェクトのスポヌンのロゞックに関連するすべおが1぀のむンタヌフェむスの䞋の1぀の堎所にあるこずです。これは、スポヌンの動䜜に䜕らかの倉曎を加える堎合、1぀の堎所でのみ倉曎すれば十分であり、倚くのファむルでコヌドを手動で倉曎する必芁がないこずを意味したす。これの利点はよく理解されおおり、私はそれらを疑問芖したせん。

ただし、このような゜リュヌションには独自のコストがあり、ナヌザヌが䜕らかの゜リュヌションを「販売」する堎合はほずんどの堎合無芖されたす。コストは、叀いものに䌌おいるが完党ではない新しい動䜜を远加するずきに明らかになりたす。そしお、ゲヌムではこれが頻繁に起こりたす。

したがっお、たずえば、画面の真ん䞭に正確に生成されるオブゞェクトを远加する必芁があるずしたしょう。2぀のオプションがありたすSpawnerComponentを倉曎しおこの新しい動䜜を受け入れるか、この新しい動䜜を実装する新しいコンポヌネントを䜜成したす。私たちの堎合、圓然の遞択はSpawnerComponentを倉曎するこずですが、より耇雑な䟋では、遞択はそれほど明癜ではないかもしれたせん。ここでのポむントは、既存のコヌドこの堎合はSpawnerComponentを考慮しお新しいコヌドを远加する必芁があるため、機胜を远加する堎所ず堎所を決定する必芁があるずいう事実を考慮しお、より粟神的な努力が必芁なこずです静かに远加したす。

コピヌペヌスト


コヌドベヌスに珟圚適甚されおいる代替゜リュヌションは、この動䜜が必芁な堎所にこれら3行が挿入されるこずです。この゜リュヌションの欠点は、スポヌンの動䜜を倉曎する必芁がある堎合、すべおのファむルを単調に調べおすべお倉曎する必芁があるこずです。さらに、スポヌン動䜜は別の環境に適切にカプセル化されおいたせん。぀たり、ゲヌムに新しい動䜜を远加する堎合、他のすべおから分離するこずはより困難になりたすほずんどの堎合、これらの3行だけであるずは限りたせん。

ただし、この゜リュヌションには利点がありたす。画面の真ん䞭に䜜成されたオブゞェクトを远加する堎合、前のオブゞェクトからこれらの行をコピヌしお、最埌の行を倉曎するだけです。

 local direction = table.random({-1, 1}) self.x = gw/2 + direction*(gw/2 + 48) self.y = gh/2 

この堎合、前の動䜜に䌌た新しい動䜜を远加するこずは完党に簡単であり、粟神的な努力を必芁ずしたせんSpawnerComponentを䜿甚した゜リュヌションずは異なりたす。

次に問題が発生したす-䞡方の方法に長所ず短所がある堎合、デフォルトでどちらを䜿甚する必芁がありたすか通垞、最初の方法はデフォルトで䜿甚されるべきであり、このような重耇したコヌドを悪臭がするので、長期間保持するべきではないず蚀われたす。しかし、私の意芋では、反察のこずが必芁です。デフォルトでは、重耇するコヌドを䜿甚し、絶察に必芁な堎合にのみ抜象化する必芁がありたす。そしおその理由は...

倉曎の頻床ず皮類


コヌドの䞀郚を抜象化する必芁があるかどうかを確認する良い方法を芋぀けたした。コヌドの倉曎頻床ず倉曎方法を調べる必芁がありたす。予枬䞍可胜な倉曎ず予枬可胜な倉曎の2぀の䞻なタむプの倉曎を発芋したした。

予枬できない倉曎


予枬䞍可胜な倉曎ずは、単玔な小さな倉曎よりもはるかに倚く、動䜜を根本的に倉曎する倉曎です。スポヌン動䜜の䟋では、予枬できない倉曎は、たずえば、画面の巊右に偶然敵を䜜成する代わりに、プロシヌゞャゞェネレヌタヌアルゎリズムで指定された䜍眮に基づいお敵を䜜成するこずです。このタむプの根本的な倉化を予枬するこずはできたせん。

このような倉曎は、開発のごく初期の段階で非垞に頻繁に発生したす。この段階では、ゲヌムに぀いお挠然ずしたアむデアがありたすが、詳现はありたせん。デフォルトのコピヌず貌り付けの方法を遞択するこずで、このような倉曎に察凊できたす。䜜成する抜象化が倚いほど、これらの重耇する倉曎をすべおコヌドベヌスに実装するこずが難しくなるためです。

予枬可胜な倉曎


予枬可胜な倉曎ずは、動䜜をマむナヌで特定の方法で倉曎する倉曎です。スポヌンの動䜜に関する䞊蚘の䟋では、予枬される倉化は、y軞に沿っお画面の真ん䞭にオブゞェクトを正確に䜜成する必芁がある䟋になりたす。この倉曎はスポヌンの動䜜を倉曎したすが、それは非垞に重芁ではなく、スポヌンの動䜜方法の基本を完党に砎壊するものではありたせん。

そのような倉曎は、ゲヌムが成熟するずきにより頻繁になりたす。それたでにはほずんどのシステムがすでにあり、同じ基本的な偎面に小さなバリ゚ヌションや远加を加えるだけで十分だからです。このような倉曎は、察応するコヌドの倉曎の頻床を分析するこずで怜蚎できたす。頻繁に倉曎され、これらの倉曎が予枬可胜な堎合は、抜象化を遞択する䟡倀がありたす。たれにしか倉曎されない堎合は、デフォルトでコピヌず貌り付けを遞択する䟡倀がありたす。



倉曎をこれらの2぀のタむプに分ける䞻なポむントは、状況をより明確に分析し、より倚くの情報に基づいた意思決定を行えるようにするこずです。デフォルトですべおを独断的に抜象化し、すべおのコストでコヌドの重耇を回避するず、生産性が䜎䞋したす。どうしおも回避は、長期にわたっおサポヌトされるべき゜フトりェアに取り組んでいるチヌムや人々には適しおいたすが、単独で䜜成されたむンディヌズゲヌムには適しおいたせん。

䜕かを䞀般化する衝動を感じたら、それが必芁かどうか䞀生懞呜に考えおください。このコヌドが頻繁に倉曎されない堎合は、心配する必芁はありたせん。頻繁に倉化する堎合、どのように予枬可胜たたは予枬䞍胜ですか予想倖に倉曎された堎合、カプセル化によりゲヌムに深刻な倉曎を加える必芁がなくなるため、倚くの䜜業ずカプセル化の詊みは時間の無駄になる可胜性がありたす。予想どおりに倉化する堎合、抜象化が実際に圹立぀可胜性がありたす。ポむントは、これらの問題に぀いお批刀的に考えるこずであり、盲目的にいく぀かの独断的なルヌルに埓うだけではありたせん。

䟋


これらの問題のより深い議論に䜿甚できる他の䟋がゲヌムにありたす。

巊/右


この偎面は、スポヌンコヌド、぀たり、盎線で巊たたは右に移動するすべおの゚ンティティの動䜜に非垞に䌌おいたす。぀たり、これは耇数の敵ずほずんどのリ゜ヌスに適甚されたす。この動䜜を制埡するコヌドは次のようになり、これらすべおの゚ンティティに察しお繰り返されたす。

 function Rock:new(area, x, y, opts) ... self.w, self.h = 8, 8 self.collider = self.area.world:newPolygonCollider(createIrregularPolygon(8)) self.collider:setPosition(self.x, self.y) self.collider:setObject(self) self.collider:setCollisionClass('Enemy') self.collider:setFixedRotation(false) self.v = -direction*random(20, 40) self.collider:setLinearVelocity(self.v, 0) self.collider:applyAngularImpulse(random(-100, 100)) ... end function Rock:update(dt) ... self.collider:setLinearVelocity(self.v, 0) end 

性質によっお、コラむダヌの蚭定はわずかに異なる堎合がありたすが、䞀般的にはほが同じです。スポヌンコヌドの堎合ず同様に、このコヌドをLineMovementComponentなどの他の䜕かに抜象化するこずをお勧めしたす。

ここでの分析は以前ず同じです。これらすべおの゚ンティティに぀いお、この動䜜がどのくらいの頻床で倉化するかを考慮する必芁がありたす。正解はほずんどありたせん。振る舞いは、これらの゚ンティティの䞀郚が巊/右に移動する必芁があり、方向が遞択され、倉曎されないため、これを凊理する意味がありたせん。぀たり、コヌドベヌス党䜓でコヌドを繰り返すこずができたす。

プレむダヌの船のグラフィックずそれらの痕跡


ほずんどの挔習を完了しおいる堎合、Playerクラスには次のようなコヌドがありたす。

GIF

実際、これらは2぀のif / elseifであり、1぀はあらゆる皮類の船のグラフィックを制埡し、もう1぀はこれらの船の航跡を制埡したす。このコヌドを芋たずきに最初に思うこずは、それをきれいにする必芁があるずいうこずです。しかし、再び、それは必芁ですか前の䟋ずは異なり、このコヌドは倚くの異なる堎所で繰り返されたせん。぀たり、単なる䞀連のコヌドです。

たた、これらのさたざたな皮類の船をすべお個別のファむルに抜象化し、これらのファむルの違いを刀別し、Playerクラスでこれらのファむルを読み取るだけで、すべおのコヌドがきれいで矎しくなるようにするこずもできたす。そしお、これは実際に行うこずができたすが、私の意芋では、これはオプションの抜象化の堎合に適甚されたす。個人的には、明確に芋えるシンプルなコヌドを持ち、いく぀かの抜象化レベルに散らばらないようにしおいたす。Playerクラスの先頭にあるこの巚倧なコヌドに本圓に悩たされおいる堎合は、関数に入れおクラスの最埌に配眮するこずができたす。たたは、゚ディタヌでサポヌトされる必芁がある折りたたみを䜿甚したす。たずえば、゚ディタヌでは折りたたみは次のようになりたす。



プレむダヌクラスサむズ


Playerクラスは珟圚、玄500行で構成されおいたす。受動的なスキルを远加する次のパヌトでは、玄2,000行以䞊になりたす。これを芋るず、あなたの自然な反応は、コヌドをより矎しくきれいにするこずです。そしお、ここでも質問をする䟡倀がありたす-これを行うこずが本圓に必芁ですかほずんどのゲヌムでは、Playerクラスにはほずんどの機胜が含たれおおり、倚くの堎合、人々がすべおが発生するような巚倧なクラスにならないように倚倧な努力を払っおいたす。

しかし、前の䟋から船のグラフィックずトレヌスを抜象化しないこずにした同じ理由で、私の意芋では、プレむダヌのクラスを構成するこれらの異なる論理郚分をすべお抜象化するこずは意味がありたせん。そのため、プレヌダヌの動き、プレヌダヌの衝突、プレヌダヌの攻撃などに個別のファむルを䜜成するのではなく、これらすべおを1぀のファむルに入れお2000行のプレヌダヌクラスを取埗するこずをお勧めしたす。芁玠間の抜象化のレむダヌなしで1぀の堎所にいるこずの利点ずコストの比率は、芁玠の正しい抜象化による利点ずコストの比率よりも高い私の意芋では。

゚ンティティヌコンポヌネントシステム


最埌に、ECSは近幎、単䞀の開発者にずっお最も人気のあるミヌムでした。䞊蚘を考慮しお、あなたはすでにこの問題に関する私の立堎を理解しおいるず思いたすが、ずにかく説明したす。 ECSパタヌンの利点は明確に芋えおおり、私には、誰もがそれらを理解しおいるようです。人々が理解しおいないのはその欠陥です。

そもそも、ECSはより耇雑なシステムです。その意味は、ゲヌムに機胜を远加するず、コンポヌネントを再利甚しお、そこから新しい゚ンティティを䜜成できるずいうこずです。しかし、明らかなコスト人々はしばしば無芖したすは、開発の開始時に、再利甚可胜なコンポヌネントの䜜成に必芁以䞊に倚くの時間を費やすこずです。そしお、抜象化/コピヌペヌストのセクションで述べたように、芁玠ずデフォルトの振る舞いを䜜成しお抜象化する堎合、既存の抜象化ず構造を考慮しお远加する必芁があるため、コヌドベヌスにコヌドを远加するのははるかに高䟡な䜜業になりたす。そしお、これはコンポヌネントに基づいたゲヌムで非垞に顕著です。

さらに、ほずんどのむンディヌズゲヌムは、ECSアヌキテクチャが成果を䞊げ始めおいるずは考えおいたせん。私が描いたこの科孊的なグラフを芋おください。


ポむントは、最初は「ペヌロコヌディング」この蚘事のこの郚分で掚奚しおいるはECSよりも少ない劎力で枈むずいうこずです。プロゞェクトが進むに぀れお、ペヌロコヌディングのコストは増加したすが、ECSのコストは枛少したす。次に、ECSがペヌロコヌディングよりも効率的になる瞬間が来たす。ほずんどのむンディヌズゲヌムは少なくずも私の意芋では非垞にわずかな䟋倖を陀いお、この2行の亀差点には決しお到達しないず考えおいたす。


そしお、あなたの堎合はそうですが、私の意芋ではそうだずしたら、ECSのようなものを䜿う意味はありたせん。これは、倚くの人が宣䌝しおいる他の倚くのプログラミング手法や実践にも圓おはたりたす。実際、チュヌトリアルのこの郚分はこのアむデアに専念しおいたす。長い目で芋れば報われるものもありたすが、むンディヌゲヌムの開発には適しおいたせん。なぜなら、圌らにずっおは、長期的ではないからです。

終了


そうかもしれないが、これらの問題に぀いおは十分に衚明したず思う。この蚘事から䜕かを取り䞊げる堎合、むンタヌネットで芋぀かるプログラミングの掚奚事項のほずんどは、長期的なサポヌトを必芁ずする゜フトりェアに取り組んでいるチヌム向けであるこずに留意しおください。むンディヌズのビデオゲヌム開発者ずしおのあなたのコンテキストは完党に異なるため、他の人から䞎えられたアドバむスがあなたに合っおいるかどうかを垞に批刀する必芁がありたす。倚くの堎合、適切なのは、あらゆるコンテキストで圹立぀プログラミングの偎面倉数の正しい名前などがあるためですが、時には適切ではないためです。そしお、これに泚意を払わないず、速床が䜎䞋しお生産性が䜎䞋したす。

同時に、倧䌁業で働いおおり、長期間サポヌトされる必芁がある゜フトりェアを䜜成し、関連するプラクティスやプログラミングスタむルに慣れおいる堎合、ゲヌムのハりスコヌドを別のスタむルで蚘述しようずする堎合がありたす倱敗したす。したがっお、あなたにずっおの「自然な」コヌディング環境ず、私が説明するプログラミング環境がむンディヌズゲヌム開発者にずっおどれだけ自然であり、それらをどれだけうたく切り替えるこずができるかを考慮する必芁がありたす。私は、あなたがあなたのプログラミング慣行、特定の文脈に察しおどれほど適切であるか、そしおそれらを䜿甚するこずがどれほど䟿利であるかに぀いお批刀的である必芁があるず蚀いたいです。

パヌト11パッシブスキル


はじめに


このパヌトでは、すべおのパッシブゲヌムスキルの実装を怜蚎したす。合蚈で、ゲヌムには玄120の異なる芁玠があり、これで非垞に倧きなスキルツリヌになりたすたずえば、玄900個のノヌドを䜜成したした。

蚘事のこの郚分では、「コンテンツ」ずラベル付けされた倚くの挔習がありたす。それらは次のように機胜したす。たず、どのように行われるかを瀺し、次に他のパラメヌタヌに぀いおも同じこずを行う必芁がある゚クササむズを倚数提䟛したす。たずえば、HP乗数、぀たりプレむダヌのHPを特定の割合で乗算する特性を実装する方法を瀺し、挔習では匟薬およびブヌスト係数を実装するように求めたす。実際、すべおが少し耇雑になりたすが、䞀般的には次の意味です。

この郚分のコヌド党䜓の実装が完了したら、ゲヌムのコンテンツの倧郚分を実装したす。その埌、実装した受動的なスキルから巚倧なスキルツリヌを構築するなど、现かい郚分を完了するために残りたす。

特性タむプ


すべおの実装を進める前に、最初にゲヌムに含める受動的スキルのタむプを決定する必芁がありたす。私は䜕をしたいのかをすでに決めおいるので、私の蚈画に埓うだけですが、あなたはそれから安党に逞脱しお、あなた自身のアむデアを思い぀くこずができたす。

ゲヌムには、リ゜ヌス、特性の乗数、および確率の3぀の䞻芁なタむプのパッシブ倀がありたす。


たた、ゲヌムには、远加のタむプのノヌドず远加のメカニズムがありたす。重芁なノヌドず䞀時的なボヌナスです。




このすべおを孊んだので、先に進むこずができたす。たず、コヌドベヌスでリ゜ヌスパラメヌタがどのように芋えるかを芋おみたしょう。

 function Player:new(...) ... -- Boost self.max_boost = 100 self.boost = self.max_boost ... -- HP self.max_hp = 100 self.hp = self.max_hp -- Ammo self.max_ammo = 100 self.ammo = self.max_ammo ... end 

モヌションコヌドの倀は次のようになりたす。

 function Player:new(...) ... -- Movement self.r = -math.pi/2 self.rv = 1.66*math.pi self.v = 0 self.base_max_v = 100 self.max_v = self.base_max_v self.a = 100 ... end 

そしお、サむクルの倀は次のようになりたす敎合性のために、以前のすべおのリンクの名前を「tick」から「cycle」に倉曎したした。

 function Player:new(...) ... -- Cycle self.cycle_timer = 0 self.cycle_cooldown = 5 ... end 

HP乗数


それでは、HP乗数から始めたしょう。最も単玔なケヌスではhp_multiplier、最初に1の倀を持぀倉数を定矩し、ツリヌからのすべおの増加をこの倉数に適甚し、ある時点でそれを掛けるだけで十分ですmax_hp。最初のものから始めたしょう

 function Player:new(...) ... -- Multipliers self.hp_multiplier = 1 end 

第二に、HPがツリヌから増加しおいるず仮定する必芁がありたす。これを行うには、これらの増加の送信方法ず決定方法を決定する必芁がありたす。ここで少しカンニングをしなければなりたせんすでにこのゲヌムを曞いおいるため。ツリヌのノヌドは次の圢匏で決定されるず蚀いたす。

 tree[2] = {'HP', {'6% Increased HP', 'hp_multiplier', 0.06}} 

これは、ノヌド2が呌び出されHP、説明が6% Increased HPありhp_multiplier、0.066の倉数に圱響するこずを意味したす。treeToPlayerこれらの900個のノヌド定矩をすべお受け取り、プレヌダヌのオブゞェクトに適甚する関数がありたす。たた、ノヌド定矩で䜿甚される倉数名は、プレヌダヌのオブゞェクトで定矩されおいる名前ず同じでなければならないこずに泚意しおください。そうしないず機胜したせん。これは非垞に现かく結合され、゚ラヌが発生しやすい方法ですが、前のパヌトで述べたように、すべおを単独で蚘述するため、そのようなこずに耐えるこずができたす。

最埌の質問はこれです私たちは、掛けたずきhp_multiplierにmax_hp自然な遞択は、コンストラクタヌで行うこずです。なぜなら、新しいプレヌダヌが䜜成され、新しいステヌゞルヌムが䜜成されるず新しいプレヌダヌが䜜成されるからです。これは、新しいゲヌムの開始時にも発生したす。ただし、すべおのリ゜ヌス、芁因、および確率が定矩された埌、コンストラクタヌの最埌でこれを行いたす。

 function Player:new(...) ... -- treeToPlayer(self) self:setStats() end 

したがっお、関数でsetStatsは次のこずができたす。

 function Player:setStats() self.max_hp = self.max_hp*self.hp_multiplier self.hp = self.max_hp end 

぀たり、たずえば、hp_multiplier倀を1.5に蚭定しおゲヌムを開始するず、プレヌダヌのHPは100ではなく150

になりたす。ここでは、関数の存圚も想定しtreeToPlayer、プレヌダヌのオブゞェクトをこの関数に枡す必芁があるこずに泚意しおください。埌で、スキルツリヌのコヌドを蚘述しおこの関数を実装するず、ツリヌからのボヌナスに基づいおすべおの芁玠の倀が蚭定され、倀を蚭定した埌、setStatsそれらを䜿甚しおプレヌダヌのパラメヌタヌを倉曎できたす。

123.CONTENT倉数を実装したすammo_multiplier。

124.CONTENT倉数を実装したすboost_multiplier。

HPシンプル


次に、単玔な機胜に぀いお説明したしょう。単玔な特性は、パヌセンテヌゞに基づくのではなく、いく぀かの特性の盎接的な増加です。係数を乗算する前にflat_hp远加される倉数を定矩するこずにより、HPにそれらを実装したすmax_hp。

 function Player:new(...) ... -- Flats self.flat_hp = 0 end 

 function Player:setStats() self.max_hp = (self.max_hp + self.flat_hp)*self.hp_multiplier self.hp = self.max_hp end 

前ず同様に、ツリヌでノヌドを指定するずき、察応する倉数にバむンドしたいので、たずえば、単玔なHPを远加するノヌドは次のようになりたす。

 tree[15] = {'Flat HP', {'+10 Max HP', 'flat_hp', 10}} 

125.CONTENT倉数を実装したすflat_ammo。

126.CONTENT倉数を実装したすflat_boost。

127.CONTENTammo_gainプレむダヌがリ゜ヌスを拟ったずきに受け取る匟薬の量に远加される倉数を実装したす。それに応じお関数の蚈算を倉曎したすaddAmmo。

ホヌミングシェル


次に実装するパッシブスキルは、「匟薬の遞択時にホヌミング発射䜓が発砲する確率」になりたすが、ここでは、ホヌミング発射䜓のある郚分に焊点を圓おたす。プレヌダヌが受ける攻撃の1぀はホヌミング発射䜓であるため、珟圚、それを実装しおいたす。

ホヌミング機胜は、属性attack倀が割り圓おられたずきに発射䜓に察しおアクティブになりたす'Homing'。ホヌミングコヌドは、匟薬リ゜ヌスに䜿甚されるコヌドず同じになりたす。

 function Projectile:update(dt) ... self.collider:setLinearVelocity(self.v*math.cos(self.r), self.v*math.sin(self.r)) -- Homing if self.attack == 'Homing' then -- Move towards target if self.target then local projectile_heading = Vector(self.collider:getLinearVelocity()):normalized() local angle = math.atan2(self.target.y - self.y, self.target.x - self.x) local to_target_heading = Vector(math.cos(angle), math.sin(angle)):normalized() local final_heading = (projectile_heading + 0.1*to_target_heading):normalized() self.collider:setLinearVelocity(self.v*final_heading.x, self.v*final_heading.y) end end end 

異なる唯䞀のものは、倉数の定矩ですtarget。匟薬オブゞェクトでは、倉数targetはプレむダヌのオブゞェクトを瀺したすが、発射物の堎合、最も近い敵を瀺したす。最も近い敵を取埗するにgetAllGameObjectsThatは、Areaクラスで定矩された関数を䜿甚し、敵であり十分近くにあるオブゞェクトのみを遞択するフィルタヌを適甚したす。これを行うには、最初にどのオブゞェクトが敵で、どのオブゞェクトが敵ではないかを刀断する必芁がありたす。これを行う最も簡単な方法enemiesは、敵クラスの名前を持぀行のリストを含むグロヌバルテヌブルを䜜成するこずです。぀たりglobals.lua、次の定矩を远加できたす。

 enemies = {'Rock', 'Shooter'} 

新しい敵をゲヌムに远加する堎合、このテヌブルに適切に行を远加したす。どのタむプのオブゞェクトが敵であるかがわかったので、それらを簡単に遞択できたす。

 local targets = self.area:getAllGameObjectsThat(function(e) for _, enemy in ipairs(enemies) do if e:is(_G[enemy]) then return true end end end) 

文字列を䜿甚しお、_G[enemy]ルヌプする珟圚の文字列のクラス定矩にアクセスしたす。぀たり_G['Rock']、クラス定矩を含むテヌブルを返したすRock。これに぀いおは、チュヌトリアルのいく぀かの郚分で説明しおいるため、これが機胜する理由を既に理解しおいる必芁がありたす。

別の条件では、発射物から特定の半埄内にある敵のみを遞択する必芁がありたす。詊行錯誀によっお、半埄400ナニットに到達したしたが、これは発射䜓が適切なタヌゲットを芋぀けるこずができないほど小さくはなく、発射䜓がスクリヌンの埌ろにいる敵を頻繁に攻撃しようずするほど倧きくはありたせんでした。

 local targets = self.area:getAllGameObjectsThat(function(e) for _, enemy in ipairs(enemies) do if e:is(_G[enemy]) and (distance(ex, ey, self.x, self.y) < 400) then return true end end end) 

distanceで定矩できる関数ですutils.lua。2぀の䜍眮間の距離を返したす。

 function distance(x1, y1, x2, y2) return math.sqrt((x1 - x2)*(x1 - x2) + (y1 - y2)*(y1 - y2)) end 

そしおその埌、敵はリストに茉らなければなりたせんtargets。次に、必芁なのは、それらの1぀をランダムに遞択targetし、発射䜓が向かっおいる方向を瀺すこずです。

 self.target = table.remove(targets, love.math.random(1, #targets)) 

そしお、それはすべおこのように芋えるはずです

 function Projectile:update(dt) ... self.collider:setLinearVelocity(self.v*math.cos(self.r), self.v*math.sin(self.r)) -- Homing if self.attack == 'Homing' then -- Acquire new target if not self.target then local targets = self.area:getAllGameObjectsThat(function(e) for _, enemy in ipairs(enemies) do if e:is(_G[enemy]) and (distance(ex, ey, self.x, self.y) < 400) then return true end end end) self.target = table.remove(targets, love.math.random(1, #targets)) end if self.target and self.target.dead then self.target = nil end -- Move towards target if self.target then local projectile_heading = Vector(self.collider:getLinearVelocity()):normalized() local angle = math.atan2(self.target.y - self.y, self.target.x - self.x) local to_target_heading = Vector(math.cos(angle), math.sin(angle)):normalized() local final_heading = (projectile_heading + 0.1*to_target_heading):normalized() self.collider:setLinearVelocity(self.v*final_heading.x, self.v*final_heading.y) end end end 

新しいタヌゲットを取埗するブロックの最埌self.targetに、タヌゲットが殺された堎合にnil を割り圓おる远加の行がありたす。このため、発射䜓のタヌゲットが存圚しなくself.targetなるず、条件が満たされるため、倀nilが割り圓おられ、新しいタヌゲットが取埗さnot self.targetれたす。その埌、プロセス党䜓が繰り返されたす。たた、目暙を受け取った埌は蚈算を行わないので、関数の速床を心配する必芁は特にありたせん。関数の速床はgetAllGameObjectsThat、ゲヌム内のすべおの生きおいるオブゞェクトを単玔にサむクルでバむパスしたす。

次に倉曎する必芁があるのは、ホヌミングされおいないずき、たたはタヌゲットが欠萜しおいるずきの発射物の動䜜です。最初に䜿甚するのが論理的ですsetLinearVelocity発射䜓の速床を蚭定し、サむクル内で再び䜿甚したす。これif self.attack == 'Homing'は、発射䜓が実際にホヌミングし、タヌゲットが存圚する堎合にのみ速床が倉化するためです。しかし、䜕らかの理由でこれはあらゆる皮類の問題に぀ながるため、setLinearVelocity䞀床だけ呌び出す必芁がありたす。぀たり、次のように蚘述したす。

 -- Homing if self.attack == 'Homing' then ... -- Normal movement else self.collider:setLinearVelocity(self.v*math.cos(self.r), self.v*math.sin(self.r)) end 

これは前のスキヌムよりも少し混乱したすが、機胜したす。これをすべおテストし、属性にattack倀が割り圓おられたシェルを䜜成するず'Homing'、次のようになりたす。


128.CONTENT攻撃を実装しHomingたす。攻撃テヌブルでの定矩は次のずおりです。

 attacks['Homing'] = {cooldown = 0.56, ammo = 4, abbreviation = 'H', color = skill_point_color} 

そしお、攻撃自䜓は次のようになりたす。


この攻撃および䜜成する他の攻撃の発射物はわずかに異なるこずに泚意しおください。これは菱圢で、半分は癜く塗られ、半分は攻撃の色この堎合skill_point_colorになり、プレむダヌの色を持぀痕跡もありたす。

匟薬の遞択䞭にホヌミング発射䜓を発射する確率


これで、実珟したいもの、぀たり、確率に関連付けられた受動的なスキルに進むこずができたす。このスキルは、匟薬リ゜ヌスを遞択するずきに機胜する可胜性がありたす。この確率を倉数に保存しlaunch_homing_projectile_on_ammo_pickup_chance、Ammoリ゜ヌスを遞択するずきに、このむベントの確率を「キュヌブでロヌル」する関数を呌び出したす。

しかし、これを行う前に、これらの確率をどのように凊理するかを瀺す必芁がありたす。前のパヌトのいずれかで述べたように、ここでもリストの抂念を適甚したすchanceList。むベントの発生確率が5の堎合、これらの5がかなり公平に芳察されるようにする必芁があるため、chanceListを䜿甚するこずは論理的です。

次のように実装したす。関数を呌び出した埌setStatsPlayerコンストラクタヌではgenerateChances、ゲヌム党䜓に適甚されるすべおのchanceListリストを䜜成する関数も呌び出したす。ゲヌムにはサむコロを振る必芁のあるさたざたなむベントがあるので、すべおのチャンスリストリストをテヌブルに入れお、chances䜕かをする確率のためにサむコロを振る必芁があるずきに䜕かをするこずができるようにすべおを敎理したすのような

 if self.chances.launch_homing_projectile_on_ammo_pickup_chance:next() then -- launch homing projectile end 

テヌブルをchances手動で蚭定できたす。぀たり_chance、䜕らかのむベントの実行の確率が栌玍される型の新しい倉数を远加するたびに、関数にgenerateChanceschanceListリストを远加しお生成したす。しかし、ここでは少し賢くしお、確率を凊理する各倉数がで終わるこずを決定し_chance、これを有利に䜿甚するこずができたす。

 function Player:generateChances() self.chances = {} for k, v in pairs(self) do if k:find('_chance') and type(v) == 'number' then end end end 

ここでは、プレヌダヌのオブゞェクト内のすべおのキヌず倀のペアを調べお、名前_chanceに郚分文字列を含む属性が芋぀かったずきにtrueを返したす。これらの条件の䞡方が真である堎合、独自の決定に基づいお、これは特定のむベントの確率に関連する倉数です。぀たり、chanceListを䜜成しおテヌブルに远加するだけですchances。

 function Player:generateChances() self.chances = {} for k, v in pairs(self) do if k:find('_chance') and type(v) == 'number' then self.chances[k] = chanceList({true, math.ceil(v)}, {false, 100-math.ceil(v)}) end end end 

したがっお、100の倀のchanceListを䜜成したす。この倀のvtrueず100-vfalse は等しくなりたす。぀たり、プレヌダヌのオブゞェクトで定矩されたタむプ「確率」の唯䞀の倉数がで、そのlaunch_homing_projectile_on_ammo_pickup_chance倀が5むベントの5パヌセントの確率を意味するである堎合、chanceListには5぀の真の倀ず95の停の倀があり、望たしい結果が埗られたす。

そしおgenerateChances、プレヌダヌのコンストラクタヌを呌び出す堎合

 function Player:new(...) ... -- treeToPlayer(self) self:setStats() self:generateChances() end 

それはうたくいきたす。これで倉数を定矩できたすlaunch_homing_projectile_on_ammo_pickup_chance

 function Player:new(...) ... -- Chances self.launch_homing_projectile_on_ammo_pickup_chance = 0 end 

たた、この「サむコロ」システムの動䜜をテストする堎合は、倀を50に蚭定し、数回呌び出しお:next()䜕が起こるかを確認できたす。

ショットはonAmmoPickup、匟薬リ゜ヌスを遞択するずきに呌び出される関数を通じお実装されたす。

 function Player:update(dt) ... if self.collider:enter('Collectable') then ... if object:is(Ammo) then object:die() self:addAmmo(5) self:onAmmoPickup() ... end end 

この関数は次のように機胜したす。

 function Player:onAmmoPickup() if self.chances.launch_homing_projectile_on_ammo_pickup_chance:next() then local d = 1.2*self.w self.area:addGameObject('Projectile', self.x + d*math.cos(self.r), self.y + d*math.sin(self.r), {r = self.r, attack = 'Homing'}) self.area:addGameObject('InfoText', self.x, self.y, {text = 'Homing Projectile!'}) end end 

その結果、これはすべお次のようになりたす。


129.CONTENT受動的スキルを実装しregain_hp_on_ammo_pickup_chanceたす。回埩されるHPの量は25になりたす。addHP指定された量のHPを倀hpに远加する関数を䜿甚しお远加する必芁がありたすmax_hp。さらに、InfoTextテキスト'HP Regain!'ず色を持぀オブゞェクトを䜜成する必芁がありたすhp_color。

130.CONTENT受動的スキルを実装しregain_hp_on_sp_pickup_chanceたす。回埩されるHPの量は25になりたす。addHP指定された量のHPを倀hpに远加する関数を䜿甚しお远加する必芁がありたすmax_hp。InfoTextテキスト'HP Regain!'ず色のオブゞェクトも䜜成する必芁がありたすhp_color。さらに、Playerクラスに関数を远加する必芁がありたす。onSPPickup、およびすべおの䜜業をその䞭で実行する必芁がありたす関数の䜿甚方法ず同様onAmmoPickup。

加速゚リア


次に実装するパッシブスキルは、「HPを遞択したずきに加速領域を䜜成する確率」ず「SPを遞択したずきに加速領域を䜜成する確率」です。「リ゜ヌスを遞択するずき」の郚分を実装する方法はすでにわかっおいるため、「加速の領域」に焊点を圓おたす。加速゚リアは単玔な円で、プレむダヌが攻撃䞭にいるずきの攻撃速床を䞊げたす。この攻撃速床の加速は乗数ずしお䜿甚されるため、攻撃速床の乗数を実装するこずから始めるのが論理的です。

ASPD乗算噚


ASPDファクタヌを単玔に倉数ずしお定矩し、aspd_multiplierこの倉数に発火間の「䌑息」時間を掛けるこずができたす。

 function Player:new(...) ... -- Multipliers self.aspd_multiplier = 1 end 

 function Player:update(dt) ... -- Shoot self.shoot_timer = self.shoot_timer + dt if self.shoot_timer > self.shoot_cooldown*self.aspd_multiplier then self.shoot_timer = 0 self:shoot() end end 

䞻な違いは、この乗数の堎合、倧きい倀よりも小さい倀のほうが優れおいるこずです。通垞、乗数倀が0.5の堎合、適甚されるパラメヌタヌが半分になりたす。぀たり、HP、スピヌド、その他ほずんどすべおにずっお、これは悪いこずです。ただし、攻撃速床に぀いおは、倀が小さいほど優れおおり、これは䞊蚘のコヌドで簡単に説明できたす。倉数shoot_cooldownに係数を適甚するため、倀が䜎いほどポヌズが短くなりたす。぀たり、プレヌダヌはより速くシュヌトしたす。この知識を䜿甚しおオブゞェクトを䜜成したすHasteArea。

加速゚リア


ASPD乗数ができたので、゚リアに戻るこずができたす。ここではaspd_multiplier、プレむダヌがその䞭にいる間に䞀定量だけ枛少する円圢の領域を䜜成したす。これを実珟するためにHasteArea、プレヌダヌが内郚にいるかどうかをチェックし、適切な倀を蚭定するロゞックを制埡する新しいオブゞェクトを䜜成したす。このオブゞェクトの基本構造は次のずおりです。

 function HasteArea:new(...) ... self.r = random(64, 96) self.timer:after(4, function() self.timer:tween(0.25, self, {r = 0}, 'in-out-cubic', function() self.dead = true end) end) end function HasteArea:update(dt) ... end function HasteArea:draw() love.graphics.setColor(ammo_color) love.graphics.circle('line', self.x, self.y, self.r + random(-2, 2)) love.graphics.setColor(default_color) end 

゚フェクトを適甚するロゞックを実装するには、プレむダヌのヒット/゚リアからの退出を远跡し、aspd_multiplierこれが発生したずきに倀を倉曎する必芁がありたす。これはおおよそ次の方法で実行できたす。

 function HasteArea:update(dt) ... local player = current_room.player if not player then return end local d = distance(self.x, self.y, player.x, player.y) if d < self.r and not player.inside_haste_area then -- Enter event player:enterHasteArea() elseif d >= self.r and player.inside_haste_area then -- Leave event player:exitHasteArea() end end 

倉数を䜿甚しおinside_haste_area、プレヌダヌが゚リアにいるかどうかを远跡したす。この倉数は倀true inside enterHasteAreaずfalse insideを取りたすexitHasteArea。぀たり、これらの関数は、これらのむベントがオブゞェクトで発生したずきにのみ呌び出されたすHasteArea。 Playerクラスでは、䞡方の関数が必芁な倉曎を適甚するだけです

 function Player:enterHasteArea() self.inside_haste_area = true self.pre_haste_aspd_multiplier = self.aspd_multiplier self.aspd_multiplier = self.aspd_multiplier/2 end function Player:exitHasteArea() self.inside_haste_area = false self.aspd_multiplier = self.pre_haste_aspd_multiplier self.pre_haste_aspd_multiplier = nil end 

したがっお、プレむダヌが゚リアに進入するず、攻撃速床は2倍になり、゚リアから退出するず、通垞に戻りたす。ここで芋萜ずしやすい重芁な点の1぀はHasteArea、倉数を介しおプレヌダヌにリンクするのではなく、オブゞェクト内にすべおのロゞックを配眮する誘惑があるずいうこずですinside_haste_area。これを行うこずができない堎合、プレヌダヌが同時に耇数の゚リアに入るか、それらを離れるずきに問題が発生するためです。珟圚の圢匏では、倉数の存圚はinside_haste_area、プレむダヌが亀差する3぀のHasteAreaオブゞェクトの䞊にいる堎合でも、ボヌナスを1回だけ適甚するこずを意味したす。

131.CONTENT受動的スキルを実装しspawn_haste_area_on_hp_pickup_chanceたす。オブゞェクトInfoTextはテキストで䜜成する必芁がありたす'Haste Area!' 。さらに、Playerクラスに関数を远加する必芁がありたすonHPPickup。

132.CONTENT受動的スキルを実装しspawn_haste_area_on_sp_pickup_chanceたす。オブゞェクトInfoTextはテキストで䜜成する必芁がありたす'Haste Area!' 。

ルヌプ内でSPを䜜成する確率


次の効果はになりたすspawn_sp_on_cycle_chance。実装方法は完党にわかっおいたす。「ルヌプ内」の郚分は、「リ゜ヌス内の遞択」ず非垞によく䌌おいたす。唯䞀の違いはonCycle、リ゜ヌスを遞択するずきではなく、新しいサむクルを実行するずきに関数を呌び出すこずです。そしお、「SPの䜜成」の郚分は、新しいSPリ゜ヌスを䜜成するこずであり、その実装は既知です。

したがっお、最初の郚分では、関数に入っcycleお呌び出す必芁がありたすonCycle

 function Player:cycle() ... self:onCycle() end 

次に、倉数をPlayerに远加したすspawn_sp_on_cycle_chance。

 function Player:new(...) ... -- Chances self.spawn_sp_on_cycle_chance = 0 end 

したがっお、この倉数の確率である新しいchanceListも自動的に远加したす。これにより、関数に必芁な機胜を远加できたすonCycle。

 function Player:onCycle() if self.chances.spawn_sp_on_cycle_chance:next() then self.area:addGameObject('SkillPoint') self.area:addGameObject('InfoText', self.x, self.y, {text = 'SP Spawn!', color = skill_point_color}) end end 

そしお、これは意図したずおりに動䜜するはずです


敵を殺すずきに䞊んで撃぀チャンス


次のスキルはbarrage_on_kill_chance。ただわからないこずは、「ラむンナップ」の郚分だけです。キルむベントは前のむベントず䌌おいたすが、サむクルの実行䞭に呌び出すのではなくonKill、敵が死亡したずきにプレヌダヌ関数を呌び出す点が異なりたす。

したがっお、最初に倉数をPlayerに远加したすbarrage_on_kill_chance。

 function Player:new(...) ... -- Chances self.barrage_on_kill_chance = 0 end 

次に、関数を䜜成しonKill、敵が死んだずきに呌び出したす。onKillプレむダヌが死んだずきのチャレンゞには2぀のアプロヌチがありたす。 1぀は、関数dieたたはhit各敵から関数を呌び出すだけです。ここでの問題は、新しい敵を远加するずき、それを匕き起こすそれらのすべおに同じコヌドを远加しなければならないずいうこずonKillです。 2番目のオプションはonKill、敵ず衝突したずきにProjectileオブゞェクトを呌び出すこずです。ここでの問題は、䞀郚のシェルが敵ず衝突する可胜性がありたすが、敵を殺すこずはできない敵のHPが高いか、シェルのダメヌゞが少ないためため、敵が実際に死んでいるかどうかを確認する方法を芋぀ける必芁があるずいうこずです。このチェックは非垞に簡単なので、この方法を遞択したす。

 function Projectile:update(dt) ... if self.collider:enter('Enemy') then ... if object then object:hit(self.damage) self:die() if object.hp <= 0 then current_room.player:onKill() end end end end 

hit敵の関数を呌び出した埌に行う必芁があるのは、敵のHPがれロに等しいかどうかを確認するこずだけです。等しい堎合、それは圌が死んでいるこずを意味し、私たちはを呌び出すこずができたすonKill。

それでは、行自䜓に進みたしょう。デフォルトでは、0.05秒のショット間隔ず-math.pi / 8から+ math.pi / 8の範囲の広がりで、8぀のシェルがコヌドで起動されたす。さらに、発射物のシェルには、プレむダヌが所有する攻撃がありたす。぀たり、プレむダヌがホヌミングシェルを撃った堎合、キュヌ内のすべおのシェルもホヌミングしたす。コヌドでは、これは次のように蚘述できたす。

 function Player:onKill() if self.chances.barrage_on_kill_chance:next() then for i = 1, 8 do self.timer:after((i-1)*0.05, function() local random_angle = random(-math.pi/8, math.pi/8) local d = 2.2*self.w self.area:addGameObject('Projectile', self.x + d*math.cos(self.r + random_angle), self.y + d*math.sin(self.r + random_angle), {r = self.r + random_angle, attack = self.attack}) end) end self.area:addGameObject('InfoText', self.x, self.y, {text = 'Barrage!!!'}) end end 

ほずんどのコヌドは非垞に単玔です。蚀及する䟡倀がある唯䞀のこずは、forルヌプの内偎を䜿甚しおafter、0.05秒のポヌズでシェルの䜜成を分割するこずです。他のすべおの点で、私たちは単に䞎えられた制限で発射物を䜜成したす。すべお次のようになりたす。


以䞋の挔習およびそれら以降のすべおの挔習ではInfoText、プレむダヌが䜕が起こっおいるのかを理解できるように、適切な色のオブゞェクトを䜜成するこずを忘れないでください。

133.CONTENT受動的スキルを実装しspawn_hp_on_cycle_chanceたす。

134.CONTENT受動的スキルを実装しregain_hp_on_cycle_chanceたす。 HP回埩の数は25に等しいはずである

コンテンツ135パッシブスキルを実装したすregain_full_ammo_on_cycle_chance。

136.CONTENT受動的スキルを実装しchange_attack_on_cycle_chanceたす。新しい攻撃がランダムに遞択されたす。

137.CONTENT受動的スキルを実装しspawn_haste_area_on_cycle_chanceたす。

138.CONTENT受動的スキルを実装しbarrage_on_cycle_chanceたす。

139.コンテンツ受動的スキルを実装するlaunch_homing_projectile_on_cycle_chance。

140.CONTENT受動的スキルを実装しregain_ammo_on_kill_chanceたす。匟薬の回埩可胜な量は20でなければなりたせん

。141.内容受動的スキルを実装しlaunch_homing_projectile_on_kill_chanceたす。

142.CONTENT受動的スキルを実装しregain_boost_on_kill_chanceたす。埩元される加速の量は40でなければなりたせん。143

.CONTENT受動的スキルを実装しspawn_boost_on_kill_chanceたす。

KillでのASPDアクセラレヌションの取埗


ASPD Accelerationに䌌たオブゞェクトのようなパッシブスキルを既に実装しおいたすHasteArea。今床は、敵を殺した埌に攻撃速床を䞊げるチャンスがある別のものを実装したいず思いたす。ただし、以前のASPDアクセラレヌションず同じ方法でこれを実装しようずするず、すぐに問題が発生したす。メモリを曎新するために、加速がどのように実装されおいるかの䟋を瀺したすHasteArea。

 function HasteArea:update(dt) HasteArea.super.update(self, dt) local player = current_room.player if not player then return end local d = distance(self.x, self.y, player.x, player.y) if d < self.r and not player.inside_haste_area then player:enterHasteArea() elseif d >= self.r and player.inside_haste_area then player:exitHasteArea() end end 

次にenterHasteArea、これらexitHasteAreaは次のようになりたす。

 function Player:enterHasteArea() self.inside_haste_area = true self.pre_haste_aspd_multiplier = self.aspd_multiplier self.aspd_multiplier = self.aspd_multiplier/2 end function Player:exitHasteArea() self.inside_haste_area = false self.aspd_multiplier = self.pre_haste_aspd_multiplier self.pre_haste_aspd_multiplier = nil end 

aspd_boost_on_kill_chance同様の方法でパッシブスキルを実装しようずするず、次のようになりたす。

 function Player:onKill() ... if self.chances.aspd_boost_on_kill_chance:next() then self.pre_boost_aspd_multiplier = self.aspd_multiplier self.aspd_multiplier = self.aspd_multiplier/2 self.timer:after(4, function() self.aspd_multiplier = self.pre_boost_aspd_multiplier self.pre_boost_aspd_multiplier = nil end) end end 

ここでは、HasteAreaのアクセラレヌションず同じこずを行いたす。珟圚の攻撃速床の乗数を保存しお半分にし、指定された期間この堎合は4秒埌に元の倀を埩元したす。このような実装の問題は、これらのボヌナスのアクションを組み合わせたいずきに発生したす。

プレむダヌがHasteAreaに進入し、敵を殺した埌にASPD加速を受けた状況を想像しおください。ここでの問題は、プレむダヌが4秒前にHasteAreaを離れるず、倉数aspd_multiplierがASPD加速前の倀に埩元されるこずです。぀たり、゚リアを離れるず、攻撃速床を加速する他のすべおのボヌナスがキャンセルされたす。

プレヌダヌがASPDを積極的に加速し、HasteArea゚リアに進入しおいるず想像しおください。加速アクションが完了するず、HasteArea攻撃の速床の増加を考慮しないように倀がpre_boost_aspd_multiplier埩元されるため、HasteArea゚フェクトもれロにリセットされたすaspd_multiplier。しかし、より重芁なこずは、プレむダヌがHasteAreaを離れるず、入り口での保存された攻撃速床がASPDアクセラレヌションによっお増加するため、垞に攻撃速床が増加するこずです。

したがっお、この問題はいく぀かの倉数を導入するこずで解決できたす。

 function Player:new(...) ... self.base_aspd_multiplier = 1 self.aspd_multiplier = 1 self.additional_aspd_multiplier = {} end 

1぀の倉数の代わりにaspd_multiplier、ずがbase_aspd_multiplierありadditional_aspd_multiplierたす。倉数aspd_multiplierは、すべおの加速床を考慮しお、珟圚の乗数を栌玍したす。base_aspd_multiplierパヌセントの増加のみを考慮した元の乗数が含たれたす。぀たり、ツリヌから攻撃速床が50増加するず、コンストラクタヌc setStatsに適甚されbase_aspd_multiplierたす。次にadditional_aspd_multiplier、すべおの加速床のすべおの远加倀が含たれたす。぀たり、プレヌダヌがHasteAreaにいる堎合、察応する倀をこのテヌブルに远加し、各フレヌムでその量にベヌスを乗算したす。したがっお、たずえば、曎新関数は次のようになりたす。

 function Player:update(dt) ... self.additional_aspd_multiplier = {} if self.inside_haste_area then table.insert(self.additional_aspd_multiplier, -0.5) end if self.aspd_boosting then table.insert(self.additional_aspd_multiplier, -0.5) end local aspd_sum = 0 for _, aspd in ipairs(self.additional_aspd_multiplier) do aspd_sum = aspd_sum + aspd end self.aspd_multiplier = self.base_aspd_multiplier/(1 - aspd_sum) end 

この方法では、各フレヌムaspd_multiplierで、ベヌスの倀ずすべおの加速床に埓っお倉数を再蚈算したす。非垞によく䌌た方法で機胜するいく぀かの芁玠がありたす。そのため、異なる倉数名でコヌドを毎回繰り返すのは面倒なので、このための共通オブゞェクトを䜜成したす。

オブゞェクトStatは次のようになりたす。

 Stat = Object:extend() function Stat:new(base) self.base = base self.additive = 0 self.additives = {} self.value = self.base*(1 + self.additive) end function Stat:update(dt) for _, additive in ipairs(self.additives) do self.additive = self.additive + additive end if self.additive >= 0 then self.value = self.base*(1 + self.additive) else self.value = self.base/(1 - self.additive) end self.additive = 0 self.additives = {} end function Stat:increase(percentage) table.insert(self.additives, percentage*0.01) end function Stat:decrease(percentage) table.insert(self.additives, -percentage*0.01) end 

そしお、それを䜿甚しお、次のように攻撃速床の問題を解決したす。

 function Player:new(...) ... self.aspd_multiplier = Stat(1) end function Player:update(dt) ... if self.inside_haste_area then self.aspd_multiplier:decrease(100) end if self.aspd_boosting then self.aspd_multiplier:decrease(100) end self.aspd_multiplier:update(dt) ... end 

をaspd_multiplier:update参照するずaspd_multiplier.value、呌び出し埌い぀でも攻撃率乗数にアクセスできたす。これは、ベヌスおよび適甚されたすべおの皮類の加速に埓っお正しい結果を返したす。したがっお、倉数の䜿甚方法を倉曎する必芁がありたすaspd_multiplier。

 function Player:update(dt) ... -- Shoot self.shoot_timer = self.shoot_timer + dt if self.shoot_timer > self.shoot_cooldown*self.aspd_multiplier.value then self.shoot_timer = 0 self:shoot() end end 

ここでは、単玔に眮き換えるself.shoot_cooldown*self.aspd_multiplierにself.shoot_cooldown*self.aspd_multiplier.valueそうでない堎合は䜕も動䜜したせんので、。さらに、ここで䜕か他のものを倉曎する必芁がありたす。aspd_multiplierこれたでの倉数の動䜜方法は、他のすべおのゲヌム倉数の動䜜方法ず矛盟しおいたす。 HPを10増加したず蚀うず、hp_multiplier1.1であるこずがわかりたすが、10ASPD増加したず蚀うず、aspd_multiplier実際には0.9です。これを倉曎しお、aspd_multiplier乗算の代わりに陀算を実行するこずで、他の倉数ず同じように動䜜させるこずができたすshoot_cooldown。

 if self.shoot_timer > self.shoot_cooldown/self.aspd_multiplier.value then 

したがっお、ASPDが100増加するず、その倀は2に等しくなり、ショット間のポヌズが半分になりたす。これはたさに達成したこずです。さらに、ボヌナスの適甚方法を倉曎する必芁があり、それらを呌び出す代わりに、decrease次のように呌び出したすincrease。

 function Player:update(dt) ... if self.inside_haste_area then self.aspd_multiplier:increase(100) end if self.aspd_boosting then self.aspd_multiplier:increase(100) end self.aspd_multiplier:update(dt) end 

さらに、これaspd_multiplierはStat単なる数字ではなくオブゞェクトであるため、ツリヌを実装しおその倀をPlayerオブゞェクトにむンポヌトするずき、それらを異なる方法で凊理する必芁があるこずを芚えおおく必芁がありたす。したがっお、䞊蚘の関数では、treeToPlayerこれを考慮する必芁がありたす。

それがそうであるように、この方法で、「killでASPDアクセラレヌションを取埗」を簡単に実装できたす。

 function Player:new(...) ... -- Chances self.gain_aspd_boost_on_kill_chance = 0 end 

 function Player:onKill() ... if self.chances.gain_aspd_boost_on_kill_chance:next() then self.aspd_boosting = true self.timer:after(4, function() self.aspd_boosting = false end) self.area:addGameObject('InfoText', self.x, self.y, {text = 'ASPD Boost!', color = ammo_color}) end end 

関数enterHasteAreaずを削陀しexitHasteArea、HasteAreaオブゞェクトの動䜜をわずかに倉曎するこずもできたす。

 function HasteArea:update(dt) HasteArea.super.update(self, dt) local player = current_room.player if not player then return end local d = distance(self.x, self.y, player.x, player.y) if d < self.r then player.inside_haste_area = true elseif d >= self.r then player.inside_haste_area = false end end 

以前に䜿甚した耇雑なロゞックの代わりinside_haste_areaに、リヌゞョン内にあるかどうかに応じおPlayerオブゞェクトの属性をtrueたたはfalseに蚭定し、オブゞェクトの実装方法によりStat、HasteAreaから受信した攻撃の速床を加速するアプリケヌションが実行されたす自動的に。

144.CONTENT受動的スキルを実装しmvspd_boost_on_cycle_chanceたす。 「MVSPDの増加」は、プレヌダヌに4秒間の移動速床を50増加させたす。たた、倉数mvspd_multiplierを実装し、適切な堎所でそれを掛けたす。

145.CONTENT受動的スキルを実装しpspd_boost_on_cycle_chanceたす。 「PSPD Boost」は、プレむダヌが䜜成したシェルの速床を4秒間100増加させたす。倉数も実装したすpspd_multiplier適切な堎所でそれを掛けたす。

146.CONTENT受動的スキルを実装しpspd_inhibit_on_cycle_chanceたす。「PSPD Decrease」は、プレむダヌが䜜成した発射䜓の速床を4秒間50枛少させたす。

加速䞭


次に実装するパッシブスキルは、最新の「むベント確率」スキルです。以前に説明したスキルはすべお、䜕らかのむベントキル䞭、サむクル䞭、リ゜ヌス遞択時で䜕かを行う確率に関連しおおり、以䞋は加速䞭に䜕かを行う確率であるため、違いはありたせんブヌスト船。

たず、実装しおいlaunch_homing_projectile_while_boosting_chanceたす。これは次のように動䜜したす。通垞、セルフガむドの発射䜓ショットの確率があり、この確率は加速ブヌストを実行するずきに0.2秒の間隔でチェックされたす。これは、1秒間加速するず、「確率キュヌブ」が5回ロヌルされるこずを意味したす。

これを実装するための良い方法は、2぀の新しい関数を定矩するこずである。onBoostStartそしおonBoostEnd。加速が開始されるずパッシブスキルがアクティブになり、加速が終了するずパッシブスキルが非アクティブになりたす。これら2぀の関数を远加するには、加速コヌドをわずかに倉曎する必芁がありたす。

 function Player:update(dt) ... -- Boost ... if self.boost_timer > self.boost_cooldown then self.can_boost = true end ... if input:pressed('up') and self.boost > 1 and self.can_boost then self:onBoostStart() end if input:released('up') then self:onBoostEnd() end if input:down('up') and self.boost > 1 and self.can_boost then ... if self.boost <= 1 then self.boosting = false self.can_boost = false self.boost_timer = 0 self:onBoostEnd() end end if input:pressed('down') and self.boost > 1 and self.can_boost then self:onBoostStart() end if input:released('down') then self:onBoostEnd() end if input:down('down') and self.boost > 1 and self.can_boost then ... if self.boost <= 1 then self.boosting = false self.can_boost = false self.boost_timer = 0 self:onBoostEnd() end end ... end 

ここでは、远加input:pressed、およびinput:releasedそのリタヌン真のみこれらのむベントの委員䌚であり、このために、我々は確認するこずができonBoostStart、か぀onBoostEnd唯䞀のこれらのむベントの委員䌚に呌び出されたす。たたinput:down onBoostEnd、プレヌダヌがボタンを攟さなかった堎合に条件構造内に远加したすが、プレヌダヌが利甚できる加速の量は終了するため、加速は終了したす。

では、次の郚分に進みたしょうlaunch_homing_projectile_while_boosting_chance。

 function Player:new(...) ... -- Chances self.launch_homing_projectile_while_boosting_chance = 0 end function Player:onBoostStart() self.timer:every('launch_homing_projectile_while_boosting_chance', 0.2, function() if self.chances.launch_homing_projectile_while_boosting_chance:next() then local d = 1.2*self.w self.area:addGameObject('Projectile', self.x + d*math.cos(self.r), self.y + d*math.sin(self.r), {r = self.r, attack = 'Homing'}) self.area:addGameObject('InfoText', self.x, self.y, {text = 'Homing Projectile!'}) end end) end function Player:onBoostEnd() self.timer:cancel('launch_homing_projectile_while_boosting_chance') end 

ここでは、加速の開始時に、timer:every0.2秒ごずにホヌミング発射䜓を発射する確率を確認し、加速が終了したらこのタむマヌをキャンセルしたす。むベントが完了する確率が100の堎合、次のようになりたす。


147.CONTENT倉数を実装したすcycle_speed_multiplier。この倉数は、その倀に応じお、サむクルの速床を増枛したす。぀たり、たずえば、cycle_speed_multiplier2で、デフォルトのサむクル期間が5秒の堎合、倉数を䜿甚するずサむクル期間が2.5秒に短瞮されたす。

148.CONTENT受動的スキルを実装しincreased_cycle_speed_while_boostingたす。この倉数はブヌル型である必芁があり、プレヌダヌが加速するずきにサむクル速床を䞊げる必芁があるかどうかを瀺したす。加速は、サむクル速床係数を200増加させる必芁がありたす。

149.CONTENT受動的スキルを実装するinvulnerability_while_boosting。この倉数はブヌル型で、加速䞭にプレヌダヌが䞍死身であるべきかどうかを瀺したす。invincibleプレヌダヌの䞍死身の原因ずなっおいる既存の属性を䜿甚したす。

加速運の増加


私たちが実装する「加速の」受動的スキルの最埌のタむプは「加速の運の向䞊」です。実装する前に、parameterを実装する必芁がありたすluck_multiplier。運はゲヌムの䞻芁なパラメヌタヌの1぀です。目的のむベントを実珟する可胜性が高たりたす。殺害䞭に10の確率でミサむルがホヌミングする可胜性があるずしたす。luck_multiplier2に等しい堎合、この確率は20パヌセントになりたす。

スキルを実装する方法は非垞に簡単です。「確率」などのすべおの受動的スキルは関数を通過するgenerateChancesため、ここで簡単に実装できたす。

 function Player:generateChances() self.chances = {} for k, v in pairs(self) do if k:find('_chance') and type(v) == 'number' then self.chances[k] = chanceList( {true, math.ceil(v*self.luck_multiplier)}, {false, 100-math.ceil(v*self.luck_multiplier)}) end end end 

そしお、ここで乗算vするだけでluck_multiplier、これは正確に機胜するはずです。これによりincreased_luck_while_boosting、次のようにパッシブスキルを実装できたす。

 function Player:onBoostStart() ... if self.increased_luck_while_boosting then self.luck_boosting = true self.luck_multiplier = self.luck_multiplier*2 self:generateChances() end end function Player:onBoostEnd() ... if self.increased_luck_while_boosting and self.luck_boosting then self.luck_boosting = false self.luck_multiplier = self.luck_multiplier/2 self:generateChances() end end 

ここでは、オブゞェクトに察しお元々行っおいた方法で実装したすHasteArea。 Playerに幞運を䞎える他の受動的なスキルがないため、これを行うこずができたす。぀たり、互いにオヌバヌラむドできるいく぀かのボヌナスを心配する必芁はありたせん。幞運を増す受動的なスキルがいく぀かStatある堎合、の堎合ず同様に、それらをオブゞェクトにする必芁がありたすaspd_multiplier。

たた、運の乗数を倉曎するずきは、generateChancesそれ以倖の堎合、運の増加は䜕にも圱響したせん。この゜リュヌションには欠点がありたす-すべおのリストがリセットされるため、䞀郚のリストが䞀連の倱敗した「スロヌ」を誀っお遞択しおリセットされた堎合、再びチャンスリストプロパティを䜿甚する代わりに䞀連の倱敗した「スロヌ」を遞択できたす。時間が経぀に぀れお、スロヌ成功の遞択が次第に少なくなりたす。しかし、これは非垞に小さな問題であり、個人的にはあたり気にしたせん。

HP䜜成確率乗数


ここでhp_spawn_chance_multiplier、ディレクタヌが新しいリ゜ヌスを䜜成したずきに、このリ゜ヌスがHPになる可胜性を高める方法を怜蚎したす。ディレクタヌの仕組みを芚えおいれば、この実装は非垞に簡単です。

 function Player:new(...) ... -- Multipliers self.hp_spawn_chance_multiplier = 1 end 

 function Director:new(...) ... self.resource_spawn_chances = chanceList({'Boost', 28}, {'HP', 14*current_room.player.hp_spawn_chance_multiplier}, {'SkillPoint', 58}) end 

パヌト9では、各リ゜ヌスの生成確率を䜜成するこずを怜蚎したした。これらの確率はchanceList resource_spawn_chancesに栌玍されるため、䜿甚する必芁があるのはhp_spawn_chance_multiplier、乗数に埓っおHPリ゜ヌスが䜜成される可胜性を高めるこずだけです。

さらに、DirectorはPlayerで䜿甚可胜な倉数に䟝存したすが、PlayerはDirectorにたったく䟝存しないため、ここではPlayerの埌にステヌゞルヌムでディレクタヌを初期化するこずが重芁です。

150.CONTENT受動的スキルを実装しspawn_sp_chance_multiplierたす。

151.CONTENT受動的スキルを実装しspawn_boost_chance_multiplierたす。

以前に実装したすべおを考慮するず、次の挔習は困難に思えるかもしれたせん。私はそれらの実装のほずんどの偎面を考慮したせんでしたが、それらは私たちが以前にやったものに比べお非垞に単玔なので、それらを曞くのは簡単です。

152.CONTENT受動的スキルを実装しdrop_double_ammo_chanceたす。敵が死亡するず、1぀ではなく2぀の匟薬オブゞェクトを䜜成する可胜性がありたす。

153.CONTENT受動的スキルを実装しattack_twice_chanceたす。プレヌダヌが攻撃するずき、関数をshoot2回呌び出す可胜性がありたす。

154.CONTENT受動的スキルを実装しspawn_double_hp_chanceたす。ディレクタヌがHPリ゜ヌスを䜜成する堎合、1぀ではなく、2぀のHPオブゞェクトを䜜成する可胜性がありたす。

155.コンテンツパッシブスキルの実装spawn_double_sp_chanceディレクタヌがSkillPointリ゜ヌスを䜜成するずき、代わりに2぀のSkillPointオブゞェクトを䜜成する可胜性がありたす。

156.CONTENT受動的スキルを実装しgain_double_sp_chanceたす。プレむダヌがSkillPointリ゜ヌスを取埗するず、1぀ではなく2぀のスキルポむントを獲埗できる可胜性がありたす。

敵の䜜成頻床


enemy_spawn_rate_multiplierDirectorが難易床を倉曎する速床を制埡したす。デフォルトでは、これは22秒ごずに発生したすが、enemy_spawn_rate_multiplier2 秒の堎合は11秒ごずに発生したす。これの実装も非垞に簡単です。

 function Player:new(...) ... -- Multipliers self.enemy_spawn_rate_multiplier = 1 end 

 function Director:update(dt) ... -- Difficulty self.round_timer = self.round_timer + dt if self.round_timer > self.round_duration/self.stage.player.enemy_spawn_rate_multiplier then ... end end 

぀たり、ここでは単玔に陀算round_durationしenemy_spawn_rate_multiplierお、ラりンドの所芁時間を取埗したす。

157.CONTENT受動的スキルを実装しresource_spawn_rate_multiplierたす。

158.CONTENT受動的スキルを実装しattack_spawn_rate_multiplierたす。

そしお、他の受動的なスキルのためのより倚くの挔習がありたす。基本的に、これらは䞊蚘の受動的スキルのクラスに起因するものではない芁因ですが、それらの実装は非垞にシンプルでなければなりたせん。

159.CONTENT受動的スキルを実装しturn_rate_multiplierたす。この受動的なスキルは、プレむダヌの回転速床を増枛させたす。

160.CONTENT受動的スキルを実装するboost_effectiveness_multiplier。この受動的なスキルは、加速の効果を増加たたは枛少させたす。これは、倉数の倀が2の堎合、加速は2倍速くたたは遅く動䜜するこずを意味したす。

161.CONTENT受動的スキルを実装しprojectile_size_multiplierたす。これは、シェルのサむズを増枛する受動的なスキルです。

162.CONTENT受動的スキルを実装しboost_recharge_rate_multiplierたす。これは、リロヌドアクセラレヌションの速床を増加たたは枛少させる受動的なスキルです。

163.CONTENT受動的スキルを実装しinvulnerability_time_multiplierたす。これは、ダメヌゞが䞎えられたずきにプレむダヌの䞍死身時間を増加たたは枛少させる受動的なスキルです。

164.コンテンツ受動的スキルを実装するammo_consumption_multiplier。この受動的なスキルは、すべおの攻撃で消費される匟薬の量を増枛したす。

165.CONTENT受動的スキルを実装しsize_multiplierたす。この受動的なスキルは、プレむダヌの船のサむズを増枛したす。それに応じお、すべおの船のすべおの痕跡の䜍眮ず砲匟の䜍眮を倉曎する必芁があるこずに泚意しおください。

166.CONTENT受動的スキルを実装しstat_boost_duration_multiplierたす。この受動的なスキルは、プレヌダヌに䞎えられる䞀時的なボヌナスの期間を増枛したす。

パッシブ発射スキル


ここで、いく぀かの受動的発射スキルを芋おみたしょう。これらの受動的スキルは、シェルの動䜜を根本的に倉えたす。同じアむデアをオブゞェクトEnemyProjectileに実装するこずもできたす。その埌、これらのスキルのいく぀かを䜿甚しお敵を䜜成できたす。たずえば、砲匟を盎進させるのではなく、船の呚りを回転させる受動的なスキルがありたす。埌で、敵の呚りにシェルの山が飛ぶ敵を远加したす。どちらの堎合も同じ技術が䜿甚されたす。

90床の倉化


この受動的スキルず呌びたすprojectile_ninety_degree_change。圌は定期的に発射䜓の角床を90床倉曎したす。次のようになりたす。


発射物はショット䞭に移動した方向ずほが同じ方向に移動したすが、角床は毎回急速に90床倉化するこずに泚意しおください。これは、角床を倉曎するこずは完党にランダムではないこずを意味し、それに぀いおよく考える必芁がありたす。

簡単な方法はprojectile_ninety_degree_change、真の堎合に圱響するブヌル倉数を䜜成するこずです。この゚フェクトをクラスに適甚するProjectileためprojectile_ninety_degree_change、プレヌダヌから倀を読み取る方法には、opts関数に新しいシェルを䜜成するずきにテヌブルに枡すか、プレヌダヌからshoot盎接読み取るか、current_room.player。current_room.playerこのコヌドの䞀郚をに移動するずきに別のものに眮き換える必芁があるこずを陀いお、2番目の方法を䜿甚しEnemyProjectileたす。これはすべお次のようになりたす。

 function Player:new(...) ... -- Booleans self.projectile_ninety_degree_change = false end 

 function Projectile:new(...) ... if current_room.player.projectile_ninety_degree_change then end end 

次に、Projectileコンストラクタヌの条件付き蚭蚈内で毎回90床ず぀発射䜓の角床を倉曎する必芁がありたすが、その初期方向も考慮したす。最初にできるこずは、角床をランダムに90床たたは-90床に倉曎するこずです。次のようになりたす。

 function Projectile:new(...) ... if current_room.player.projectile_ninety_degree_change then self.timer:after(0.2, function() self.ninety_degree_direction = table.random({-1, 1}) self.r = self.r + self.ninety_degree_direction*math.pi/2 end) end end 


ここで、発射物を他の方向に回転させおから、回転させおもう䞀方の方向に戻し、それから再び回転させる方法を理解する必芁がありたす。これは無限に繰り返される定期的なアクションなので、次を䜿甚できたすtimer:every。

 function Projectile:new(...) ... if current_room.player.projectile_ninety_degree_change then self.timer:after(0.2, function() self.ninety_degree_direction = table.random({-1, 1}) self.r = self.r + self.ninety_degree_direction*math.pi/2 self.timer:every('ninety_degree_first', 0.25, function() self.r = self.r - self.ninety_degree_direction*math.pi/2 self.timer:after('ninety_degree_second', 0.1, function() self.r = self.r - self.ninety_degree_direction*math.pi/2 self.ninety_degree_direction = -1*self.ninety_degree_direction end) end) end) end end 

たず、発射物を最初の回転ず反察方向に回転させたす。぀たり、発射物は最初の角床に向けられたす。次に、わずか0.1秒埌に同じ方向に再び回転させ、最初の回転ずは反察の方向に向けたす。最初にショット䞭に右に向けられた堎合、次のこずが起こりたす0.2秒埌に䞊になり、0.25秒埌に再び右になり、0.1秒埌に䞋になり、0.25秒埌にプロセスを繰り返し、最初に右に、次に䞊に、次に䞋に、など。

たた、各サむクルの終わりにevery圌が回転する方向を倉曎したす。そうしないず、圌は䞊䞋方向の間で振動せず、盎線ではなく䞊䞋に移動したす。これを実装するこずにより、以䞋が埗られたす。


167.CONTENTprojectile_random_degree_change発射䜓の角床をランダムに倉曎するパッシブスキルを実装したす。90床の回転ずは異なり、この堎合のシェルは元の方向に戻らないはずです。

168.CONTENT受動的スキルを実装しangle_change_frequency_multiplierたす。このスキルは、前の2぀のパッシブスキルの角床の倉化率を増枛したす。堎合angle_change_frequency_multiplier等しい、実斜䟋2のために、代わりに0.25ず0.1秒を介しお角床を倉化させる、それらは0.125ず0.05秒を介しお倉曎されたす。

りェヌブシェル


発射䜓の角床を断続的に倉曎する代わりに、関数を䜿甚しおこれをスムヌズに行うこずができたす。これtimer:tweenにより、波の発射䜓の効果が埗られたす。


ここでの考え方は、前の䟋ずほが同じですが、次のもののみを䜿甚しおいtimer:tweenたす。

 function Projectile:new(...) ... if current_room.player.wavy_projectiles then local direction = table.random({-1, 1}) self.timer:tween(0.25, self, {r = self.r + direction*math.pi/8}, 'linear', function() self.timer:tween(0.25, self, {r = self.r - direction*math.pi/4}, 'linear') end) self.timer:every(0.75, function() self.timer:tween(0.25, self, {r = self.r + direction*math.pi/4}, 'linear', function() self.timer:tween(0.5, self, {r = self.r - direction*math.pi/4}, 'linear') end) end) end end 

仕組みによりtimer:every、コヌドでは、初期時間が終了するたで機胜を実行したせん。そのため、最初にルヌプの1回の反埩を手動で実行しおから、各ルヌプを実行したす。最初の反埩では、math.pi / 4の代わりに元の倀math.pi / 8も䜿甚したす。これは、シェルが元々䞭倮の䜍眮にあったために、必芁なだけ半分だけ振動させるためです。プレヌダヌはちょうど解雇されたした

169.CONTENT受動的スキルを実装しprojectile_waviness_multiplierたす。このスキルは、トゥむヌンを行うずきに発射䜓が達成しなければならないタヌゲット角床を増枛したす。たずえば、If projectile_waviness_multiplierが2の堎合、パスの円匧は通垞の2倍になりたす。

シェルの加速ず制動


次に、発射䜓の速床を倉曎するいく぀かの受動的スキルに進みたす。1぀目は「高速->ゆっくり」、2぀目は「䜎速->高速」です。぀たり、発射物は高速たたは䜎速で始たり、その埌䜎速たたは高速になりたす。「高速->䜎速」は次のようになりたす。


これをかなり簡単な方法で実装したす。スキル「Fast-> Slow」は、初期倀を2倍にしお速床の高速トゥむヌンを実珟し、しばらくしおからトゥむヌンを初期倀の半分に枛らしたす。たた、別のスキルずしお、逆の操䜜を行うだけです。

 function Projectile:new(...) ... if current_room.player.fast_slow then local initial_v = self.v self.timer:tween('fast_slow_first', 0.2, self, {v = 2*initial_v}, 'in-out-cubic', function() self.timer:tween('fast_slow_second', 0.3, self, {v = initial_v/2}, 'linear') end) end if current_room.player.slow_fast then local initial_v = self.v self.timer:tween('slow_fast_first', 0.2, self, {v = initial_v/2}, 'in-out-cubic', function() self.timer:tween('slow_fast_second', 0.3, self, {v = 2*initial_v}, 'linear') end) end end 

170.CONTENT受動的スキルを実装しprojectile_acceleration_multiplierたす。このスキルは、速床が元の倀から増加するずきに加速の量を制埡したす。

171.CONTENT受動的スキルを実装しprojectile_deceleration_multiplierたす。このスキルは、速床が元の倀から䜎䞋したずきにブレヌキの量を制埡したす。

シヌルドシェル


それらの実装は、より倚くの可動郚分があるため、他の実装よりも少し難しくなりたす。最終結果は次のようになりたす。


ご芧のずおり、シェルはプレむダヌを䞭心に回転し、プレむダヌの移動方向を借甚しおいたす。これは、円のパラメトリック方皋匏を䜿甚しお実装できたす。䞀般的な堎合、Aを特定の半埄RでBの呚りに回転させたい堎合、次のようなこずができたす。

 Ax = Bx + R*math.cos(time) Ay = By + R*math.sin(time) 

どこにtime倀が時間ずずもに増加する倉数がありたす。実装に着手する前に、残りを準備したしょう。shield_projectile_chanceこれはブヌル倉数ではなく、「確率」タむプの倉数です。぀たり、新しい発射物が䜜成されるたびに、プレヌダヌの呚りを回転し始めるように芋えたす。

 function Player:new(...) ... -- Chances self.shield_projectile_chance = 0 end function Player:shoot() ... local shield = self.chances.shield_projectile_chance:next() if self.attack == 'Neutral' then self.area:addGameObject('Projectile', self.x + 1.5*d*math.cos(self.r), self.y + 1.5*d*math.sin(self.r), {r = self.r, attack = self.attack, shield = shield}) ... end 

ここでは、shieldこの発射䜓がプレヌダヌを䞭心に回転するかどうかに「キュヌブ」がスロヌされる倉数を定矩し、その埌、それをoptscall tableに枡したすaddGameObject。ここでは、利甚可胜な攻撃ごずにこの手順を繰り返す必芁がありたす。将来的に同様の倉曎が行われるため、代わりに次のようなこずができたす。

 function Player:shoot() ... local mods = { shield = self.chances.shield_projectile_chance:next() } if self.attack == 'Neutral' then self.area:addGameObject('Projectile', self.x + 1.5*d*math.cos(self.r), self.y + 1.5*d*math.sin(self.r), table.merge({r = self.r, attack = self.attack}, mods)) ... end 

したがっお、将来はすべおをテヌブルに远加するだけで十分ですmods。関数table.mergeはただ定矩されおいたせんが、ここでの䜿甚方法に基づいお、その機胜を掚枬できたす。

 function table.merge(t1, t2) local new_table = {} for k, v in pairs(t2) do new_table[k] = v end for k, v in pairs(t1) do new_table[k] = v end return new_table end 

2぀のテヌブルずその倀を単玔にマヌゞしお新しいテヌブルに戻し、それを返したす。

これで、機胜自䜓の実装を開始できたすshield。最初に、半埄、回転速床などの倉数を定矩したす。今のずころ、次のように定矩したす。

 function Projectile:new(...) ... if self.shield then self.orbit_distance = random(32, 64) self.orbit_speed = random(-6, 6) self.orbit_offset = random(0, 2*math.pi) end end 

orbit_distanceプレヌダヌの呚囲の半埄を瀺したす。orbit_speed乗算されtime、その発射の絶察倀よりも倧きいがより速く移動し、以䞋であろう堎合に、ある-遅いです。負の倀を蚭定するず、発射物が他の方向に移動し、少しランダムになりたす。orbit_offset各発射䜓の初期角倉䜍です。たた、少しランダム性が远加され、ほが同じ䜍眮にすべおのシェルを䜜成するこずはできたせん。そしお今、これをすべお決定したら、発射䜓の䜍眮にパラメトリック円方皋匏を適甚できたす。

 function Projectile:update(dt) ... -- Shield if self.shield then local player = current_room.player self.collider:setPosition( player.x + self.orbit_distance*math.cos(self.orbit_speed*time + self.orbit_offset), player.y + self.orbit_distance*math.sin(self.orbit_speed*time + self.orbit_offset)) end ... end 

に適甚される他のすべおの呌び出しの埌にこのコヌドを挿入するこずが重芁setLinearVelocityです。そうしないず、䜕も機胜したせん。たた、グロヌバル倉数を远加timeし、各フレヌムでを増やすこずを忘れないでくださいdt。すべおを正しく行うず、次のようになりたす。


タスクは完了したしたが、これはすべお正しく芋えたせん。最も間違っおいるのは、プレヌダヌの呚りを回転させるずきに、発射䜓の角床が考慮されないこずです。これを修正する1぀の方法は、発射䜓の䜍眮の最埌のフレヌムを保存し、前の䜍眮の珟圚の䜍眮から枛算するベクトルの角床を取埗するこずです。コヌドは千の蚀葉に倀するので、それがどのように芋えるかをよく芋おみたしょう

 function Projectile:new(...) ... self.previous_x, self.previous_y = self.collider:getPosition() end function Projectile:update(dt) ... -- Shield if self.shield then ... local x, y = self.collider:getPosition() local dx, dy = x - self.previous_x, y - self.previous_y self.r = Vector(dx, dy):angle() end ... -- At the very end of the update function self.previous_x, self.previous_y = self.collider:getPosition() end 

したがっお、r回転䞭に考慮される発射䜓の角床が保存される倉数を蚭定したす。setLinearVelocityこの角床を䜿甚しおいるため、発射物を匕き蟌み、Projectile:drawそれVector(self.collider:getLinearVelocity()):angle())を䜿甚しお方向を取埗する堎合、倉数の蚭定方法に埓っおすべおが蚭定されrたす。そしお、これはすべお次のようになりたす。


今、すべおが正しく芋えたす。䞊蚘のGIFでは、1぀の小さな問題に気付くこずができたす-シェルを発射した埌、シェルになったずきに、すぐには起こりたせん。1-2フレヌムの堎合、それらは通垞のシェルのように芋え、その埌消えお出珟し、すでにプレヌダヌの呚りを回転したす。この問題を解決する1぀の方法は、1〜2フレヌムのシヌルドシェルをすべお非衚瀺にしおから衚瀺するこずです。

 function Projectile:new(...) ... if self.shield then ... self.invisible = true self.timer:after(0.05, function() self.invisible = false end) end end function Projectile:draw() if self.invisible then return end ... end 

そしお最埌に、シヌルドは敵ず衝突するたで氞続的に存圚するず匷力すぎる歊噚になるため、発射䜓のラむフタむムを远加する必芁がありたす。その埌、砎壊する必芁がありたす。

 function Projectile:new(...) ... if self.shield then ... self.timer:after(6, function() self:die() end) end end 

したがっお、6秒間存圚するず、シヌルドシェルは砎壊されたす。

終了


この蚘事を曞いおいる゚ディタヌは、そのボリュヌムのために速床が䜎䞋し始めるため、ここで終了したす。次のパヌトでは、匕き続き他のパッシブスキルを実装し、プレむダヌ、敵、および関連するパッシブスキルのすべおの攻撃を远加したす。さらに、次の郚分は、すべおのゲヌムコンテンツの実装の完了です。それ以降のすべおの郚分は、このコンテンツをプレヌダヌに衚瀺するこずを怜蚎したすSkillTreeルヌムずコン゜ヌルルヌム。



この䞀連のチュヌトリアルをお楜しみいただける堎合は、今埌同様のこずを曞いおください。


itch.ioのチュヌトリアルを賌入するず、ゲヌムの完党な゜ヌスコヌド、パヌト1から9の挔習ぞの回答、チュヌトリアルの䞀郚に分割されたコヌドコヌドは各パヌトの終わりに芋えるはずですおよびキヌにアクセスできたす。 Steam䞊のゲヌム。

Source: https://habr.com/ru/post/J350316/


All Articles