ChatGPTで適当にゲーム開発資料を生成する
懐疑主義者の私でも感動したAI、ChatGPTです。今度はゲーム開発資料を生成するprompt engineeringを紹介しようと思います。ちなみにこの記事は自動生成により書かれてい…ません。
ゲーム開発を一度でもやった人なら知っていること。ゲームのコードを書くのは楽しいがアイテムの属性とかを一々考えるのはとても大変な苦労である。流行りのAIを使って自動生成してみましょう。英語でプロンプトを打っていきます。
まずダメージ計算式を
👉 I am creating an RPG video game. I want you to generate an interesting attack damage formula for me. The formula should take into account a range of damage for each weapon. It should also use armor, elemental weaknesses, and critical hits.
"I want to ○○."とか"I am creating a○○".のような自己紹介っぽいフレーズを使うとAIに必要な根拠を一瞬で与えられます。それから「○○を生成しろ」とストレートに言うとスムーズです。とりあえずRPGで典型的な機能を盛っておきました。
説明が返ってきます。"Repeat that explanation in Japanese."を打つと日本語に翻訳してくれます。以下はAIの説明です。
次のような攻撃ダメージの式を作成しましょう。この式は、武器ごとのダメージ範囲を考慮に入れる必要があります。また、鎧、属性の弱点、クリティカルヒットも使用する必要があります。
ここでは、攻撃ダメージのための可能な式を示します。
(
base_damage
+weapon_strength
) *critical_hit_modifier
*elemental_weakness_modifier
*armor_modifier
この式の各コンポーネントの詳細は次のとおりです。
base_damage
: これは攻撃が行うことができる最小のダメージです。weapon_strength
: これは武器の強さを表す値です。強さが高いほど、武器はより多くのダメージを与えます。critical_hit_modifier
: 攻撃がクリティカルヒットの場合、この値は1より大きくなり、ダメージが増加します。そうでなければ、1になります。elemental_weakness_modifier
: ターゲットが攻撃の属性の弱点を持っている場合、この値は1より大きくなり、ダメージが増加します。そうでなければ、1になります。armor_modifier
: この値はターゲットの鎧の有効性を表します。鎧が高ければ、攻撃のダメージが減ります。もちろん、この式はゲームの固有の需要に合わせて変更や調整することができます。さらに、ターゲットのレベル、攻撃者のレベルなどの追加の要素を追加することもできます。
悪くはないですが、もう少し自分好みへ変えてみます。
👉 Make the formula use a minimum and maximum range for the weapon's base damage. Each weapon should have these two values.
👉 Remove weapon_strength.
👉 Add elemental bonus damage too, not just weaknesses.
こんな感じになりますた。zennで綺麗に表示したいのでLaTeXへ。
👉 Write code to display the formula in LaTeX.
この式を良しとしましょう。
武器を生成する
次は武器を適当に作ってみましょう。
👉 Great. Now, generate 10 weapons for me that use this formula.
あっさり作ってくれました。しかしコピペしづらいのでMarkdown方式へ
👉 Return the weapons to me in markdown format, in a code block.
こんな感じでた
Flame Sword:
min_base_damage
: 10max_base_damage
: 20critical_hit_modifier
: 1.5elemental_bonus_modifier
: 0.5elemental_weakness_modifier
: 2.0armor_modifier
: 0.8Thunder Axe:
min_base_damage
: 15max_base_damage
: 25critical_hit_modifier
: 1.2elemental_bonus_modifier
: 0.8elemental_weakness_modifier
: 1.5armor_modifier
: 0.9Frost Spear:
min_base_damage
: 8max_base_damage
: 18critical_hit_modifier
: 1.4elemental_bonus_modifier
: 0.7elemental_weakness_modifier
: 1.8armor_modifier
: 0.85
悪くないが、属性を知りたいし、属性の弱点とかを一括化したいので
👉 Include the element of each weapon as an attribute. Remove elemental_bonus_modifier. Remove the element from the name, and make the name more interesting.
Blazing Sabre:
element
: firemin_base_damage
: 10max_base_damage
: 20critical_hit_modifier
: 1.5armor_modifier
: 0.8Thundering Battleaxe:
element
: lightningmin_base_damage
: 15max_base_damage
: 25critical_hit_modifier
: 1.5armor_modifier
: 0.9Frozen Trident:
element
: icemin_base_damage
: 8max_base_damage
: 18critical_hit_modifier
: 1.5armor_modifier
: 0.85Venomous Stiletto:
element
: poisonmin_base_damage
: 11max_base_damage
: 21critical_hit_modifier
: 1.5armor_modifier
: 0.9
なにかがつまらない時は「面白くしろ」という命令は有効です。なぜつまらないかを説明してあげるとより効きます。
今回は4まで途中で止めちゃった(タイムアウトしてしまった)ので次を要求しましょう。
👉 Continue.
JSONに変換しましょう。
👉 Give me the same data in JSON format.
[
{
"name": "Blazing Sabre",
"element": "fire",
"min_base_damage": 10,
"max_base_damage": 20,
"critical_hit_modifier": 1.5,
"armor_modifier": 0.8
},
{
"name": "Thundering Battleaxe",
"element": "lightning",
"min_base_damage": 15,
"max_base_damage": 25,
"critical_hit_modifier": 1.5,
"armor_modifier": 0.9
},
{
"name": "Frozen Trident",
"element": "ice",
"min_base_damage": 8,
"max_base_damage": 18,
"critical_hit_modifier": 1.5,
"armor_modifier": 0.85
},
{
"name": "Venomous Stiletto",
"element": "poison",
"min_base_damage": 11,
"max_base_damage": 21,
"critical_hit_modifier": 1.5,
"armor_modifier": 0.9
},
...
}
CSVも出来ます。
👉 Give me the same data in CSV.
name,element,min_base_damage,max_base_damage,critical_hit_modifier,armor_modifier
Blazing Sabre,fire,10,20,1.5,0.8
Thundering Battleaxe,lightning,15,25,1.5,0.9
Frozen Trident,ice,8,18,1.5,0.85
Venomous Stiletto,poison,11,21,1.5,0.9
Rock-solid Warhammer,earth,14,24,1.5,0.95
Wind-tossed Longbow,air,9,19,1.5,0.9
Toxic Crossbow,poison,12,22,1.5,0.9
Shocking Staff,lightning,13,23,1.5,0.85
Frosty Wand,ice,12,22,1.5,0.9
Flaming Glaive,fire,11,21,1.5,0.9
もっと生成してみます。
👉 Generate 10 new items, in CSV.
name,element,min_base_damage,max_base_damage,critical_hit_modifier,armor_modifier
Void Blade,dark,8,18,1.5,0.85
Golden Sword,light,10,20,1.5,0.8
Emerald Axe,nature,15,25,1.5,0.9
Ruby Spear,fire,12,22,1.5,0.9
Sapphire Dagger,water,9,19,1.5,0.9
Diamond Hammer,light,14,24,1.5,0.95
Platinum Bow,metal,11,21,1.5,0.9
Amethyst Crossbow,nature,13,23,1.5,0.85
Topaz Staff,fire,12,22,1.5,0.9
Garnet Wand,earth,11,21,1.5,0.9
バランス調整を
👉 I want the damage ranges to be more interesting. Change all the damage ranges such that the damage matches the form of the weapon. For example, axes should have a wide range of damage. Daggers should have a small range of damage. Do this for each genre of weapon.
name,element,min_base_damage,max_base_damage,critical_hit_modifier,armor_modifier
Blazing Sabre,fire,10,20,1.5,0.8
Thundering Battleaxe,lightning,15,30,1.5,0.9
Frozen Trident,ice,8,16,1.5,0.85
Venomous Stiletto,poison,7,14,1.5,0.9
Rock-solid Warhammer,earth,14,28,1.5,0.95
Wind-tossed Longbow,air,9,18,1.5,0.9
Toxic Crossbow,poison,12,24,1.5,0.9
Shocking Staff,lightning,13,26,1.5,0.85
Frosty Wand,ice,12,24,1.5,0.9
Flaming Glaive,fire,11,22,1.5,0.9
まあまあ。
そしてコードへ
👉 Write Go code to implement this.
type Weapon struct {
Name string
Element string
MinBaseDamage int
MaxBaseDamage int
CritModifier float64
ArmorModifier float64
}
var weapons = []Weapon{
{
Name: "Blazing Sabre",
Element: "fire",
MinBaseDamage: 10,
MaxBaseDamage: 20,
CritModifier: 1.5,
ArmorModifier: 0.8,
},
{
Name: "Thundering Battleaxe",
Element: "lightning",
MinBaseDamage: 15,
MaxBaseDamage: 30,
CritModifier: 1.5,
ArmorModifier: 0.9,
},
{
Name: "Frozen Trident",
Element: "ice",
MinBaseDamage: 8,
MaxBaseDamage: 16,
CritModifier: 1.5,
ArmorModifier: 0.85,
},
// and so on...
}
func main() {
// Use the weapon data here...
}
structは作ってくれたがダメージの実装が欲しいですね
👉 Write Go code to calculate the damage.
import (
"math/rand"
)
// CalculateDamage calculates the damage for the given weapon and target armor.
// If the criticalHitChance parameter is greater than or equal to a random number
// generated in the range [0, 100], then the attack is a critical hit.
func CalculateDamage(w Weapon, armor int, criticalHitChance int) int {
baseDamage := rand.Intn(w.MaxBaseDamage-w.MinBaseDamage) + w.MinBaseDamage
criticalHitModifier := 1.0
if rand.Intn(100) < criticalHitChance {
criticalHitModifier = w.CritModifier
}
damage := int(float64(baseDamage)*w.ArmorModifier*criticalHitModifier) - armor
if damage < 0 {
return 0
}
return damage
}
中々です。しかし属性の弱点は忘れていますね。そこらへんが曖昧だったので所略されたでしょう。
import (
"math/rand"
)
var elementModifiers = map[string]map[string]float64{
"fire": {
"fire": 0.5,
"ice": 1.5,
"nature": 1.0,
},
"ice": {
"fire": 0.5,
"ice": 0.5,
"nature": 1.0,
},
// and so on...
}
// CalculateDamage calculates the damage for the given weapon and target armor.
// If the criticalHitChance parameter is greater than or equal to a random number
// generated in the range [0, 100], then the attack is a critical hit.
func CalculateDamage(w Weapon, armor string, criticalHitChance int) int {
baseDamage := rand.Intn(w.MaxBaseDamage-w.MinBaseDamage) + w.MinBaseDamage
criticalHitModifier := 1.0
if rand.Intn(100) < criticalHitChance {
criticalHitModifier = w.CritModifier
}
elementModifier := 1.0
if em, ok := elementModifiers[w.Element]; ok {
if emm, ok := em[armor]; ok {
elementModifier = emm
}
}
damage := int(float64(baseDamage)*w.ArmorModifier*elementModifier*criticalHitModifier)
if damage < 0 {
return 0
}
return damage
}
いいね!使い方も教えてくれます。
import (
"fmt"
)
func main() {
// Choose a weapon...
weapon := weapons[0]
// Calculate the damage for the weapon, with "ice" armor and a 10% critical hit chance.
damage := CalculateDamage(weapon, "ice", 10)
fmt.Println(damage)
}
最後にリファクタをさせてみましょう。
👉 I want you to refactor the Go code to change elements to be their own type instead of a raw string.
(iotaで返ってくる)👉 Instead of using iota for the elements, usetype Element string
.
👉 Define Go constants for every element present in the items we have generated.
const (
Fire Element = "fire"
Ice Element = "ice"
// and so on...
)
type Element string
var elementModifiers = map[Element]map[Element]float64{
Fire: {
Fire: 0.5,
Ice: 1.5,
// and so on...
},
Ice: {
Fire: 0.5,
Ice: 0.5,
// and so on...
},
// and so on...
}
いつでも違う言語へ変換できます!
👉 Translate all of the code into Prolog.
👉 Refactor the Prolog code to be idiomatic. Use helper predicates and avoid ;/2.
% Elements
const(fire).
const(ice).
% and so on...
% Element modifiers
element_modifiers(fire, fire, 0.5).
element_modifiers(fire, ice, 1.5).
% and so on...
% Calculate damage
calculate_damage(Weapon, Armor, CriticalHitChance, Damage) :-
% Choose a random number between MinBaseDamage and MaxBaseDamage.
weapon_base_damage(Weapon, MinBaseDamage, MaxBaseDamage),
random_between(MinBaseDamage, MaxBaseDamage, BaseDamage),
% Check if the attack is a critical hit.
critical_hit_modifier(Weapon, CriticalHitChance, CriticalHitModifier),
% Check if the weapon has an element modifier for the target's armor.
element_modifier(Weapon, Armor, ElementModifier),
% Calculate the damage.
weapon_armor_modifier(Weapon, ArmorModifier),
Damage is integer(float(BaseDamage)*ArmorModifier*ElementModifier*CriticalHitModifier).
% Choose a random number between MinBaseDamage and MaxBaseDamage.
weapon_base_damage(Weapon, MinBaseDamage, MaxBaseDamage) :-
Weapon = weapon(_, _, MinBaseDamage, MaxBaseDamage, _, _).
% Check if the attack is a critical hit.
critical_hit_modifier(Weapon, CriticalHitChance, CriticalHitModifier) :-
(
random(100) < CriticalHitChance,
Weapon = weapon(_, _, _, _, CritModifier, _),
CriticalHitModifier is CritModifier
;
CriticalHitModifier is 1.0
).
% Check if the weapon has an element modifier for the target's armor.
element_modifier(Weapon, Armor, ElementModifier) :-
(
element_modifiers(Element, Armor, ElementModifier),
Weapon = weapon(_, Element, _, _, _, _)
;
ElementModifier is 1.0
).
% Get the armor modifier for the given weapon.
weapon_armor_modifier(Weapon, ArmorModifier) :-
Weapon = weapon(_, _, _, _, _, ArmorModifier).
const
を拾ってしまったのは「翻訳」した製?言い方次第で防げたかもしれません("Now, write Prolog code for me instead."とか)
とりあえず武器をも
👉 Generate 10 weapons for me, in Prolog.
👉 Make the names more exciting or interesting or fantasy.
weapon("Frostbite", ice, 5, 10, 1.5, 0.5).
weapon("Flameblade", fire, 10, 15, 1.2, 1.0).
weapon("Nature's Fury", nature, 15, 20, 1.3, 0.8).
weapon("Thunderstrike", lightning, 20, 25, 1.4, 0.7).
weapon("Shadowclaw", shadow, 25, 30, 1.5, 0.6).
weapon("Frostfire", ice, 30, 35, 1.6, 0.5).
weapon("Flameshot", fire, 35, 40, 1.7, 0.4).
ちなみに「Make the game's setting sci-fi.」とかでゲームのジャンアルをいつでも変えられます。
いやぁ、すごいですね… 怖いですねw
Discussion