CYF(Create Your Frisk)のちょこっとしたよくある(かもしれない)質問や疑問をここに置いておく。
いくつか個人的に気になったことを備忘録として残してもおく。
ちなみにCYF付属のドキュメントを読んで解決できることはここではほぼ書かない。
少しずつ追加していくかもしれない...
ドキュメントの見方(どこにどの機能があるか)
ドキュメントに書いてあることはほぼ書かないが、どこにどの機能に関して書いてあるか、ドキュメントの見方を軽く紹介しよう。
- Basic setup: Modのフォルダ階層や各スクリプトの概要が書かれている。
- Special Variables: 予約変数。(Encounter Skeltonで)最初から書かれている変数や、MERCYメニュー関連、Monsterスクリプトの敵のステータスなどに関わるものが多い。Encounterで使える変数に便利なものもあるので一度チェックすることをおすすめする。
- Terminology: 用語集。ドキュメントで意味の分からない単語があったら見るページ。さほど見ない。
API
- Text commands: テキストの効果(色やエフェクト(揺らすなど)、スキップできないようにする、声を変えるなど)や、テキスト内で関数を呼び出す方法など。
- Game events: (Encounter Skeltonで)最初から書かれている関数(ゲームイベント)について書いてある。いくつか追加できるものがあるので、一度見てみることをおすすめする。
Functions & Objects
- Misc. Functions: その他関数。どこでも使える関数や、ゲームの流れを変える関数、セーブロードの関数、敵の状態を変える関数などがある。
- The Player Object: プレイヤーのステータスや、ソウルの位置に関わるクラス。強制攻撃の関数もある。
- The Script Object: (量が無いので)さほど見ないが、他スクリプト(基本的にEncounter)の値を変える方法が書かれている。他スクリプトの関数を呼び出すこともできる。
- The Audio Object: 音楽(BGM)やサウンド(SE)に関わるクラス。デフォルトで登録されている音を変える方法も書かれている。
-
The NewAudio Object: 上記の
The Audio Object
の強化版。 - The Input Object: 入力関係のクラス。他の色ソウルや、メニューなどを作る場合はこれが必須。
- The Time Object: 見ないな。フレームベースなコードを書いている私にとっては不要。
- The Inventory Object: アイテム関係のクラス。アイテムを追加したり、プレイヤーのインベントリを変えたりできる。
-
The Misc Object: ウインドウや画面の制御(動かしたり揺らしたり)やファイル操作のクラス。
AlMightyGlobal
を使用したセーブロードが嫌いな方はこちらをどうぞ。 - The Discord Object: Discordとの連携について。
- The Arena Object: Waveスクリプト限定(AsteriskModではStatesスクリプト含む)のアリーナを動かしたりサイズ変更したりするクラス。
- Projectile Management: Waveスクリプトに限らないが、Waveスクリプトの作成で役に立つ、発射体(弾丸)オブジェクトに関して書かれている。
-
The Pixel-Perfect Collision System: 上記の発射体オブジェクトの
PPCollision
に関しての説明。 - Sprites & Animation: スプライトの作成と制御方法。
- The Text Object: テキストの作成と制御方法。
Resources
- Dialog bubble names: 吹き出し名一覧
-
Key List:
Input.GetKey()
の引数に入れられるもの(ほぼ)一覧。
Sharders? Overworld...? 知らないよ...そんなの
エラー関連
Luaの文法が間違ってるとか、スペルミスなどが原因とか、どちらかと言うとCYFの問題ではないのだがよくあるエラーはここに置いておこう。
※ 以下で「nil
が代入されている」と言う表現があるが、これは未定義やスペルミスで存在しないことや、文字通りnilを代入していることを表す。
(動的言語であるLuaでは未定義の変数(関数)は全てnil
である。)
しっかり定義したといっても、定義する前に読み込もうとしたらアウトだぞ。
local framecounter = 0
function Update()
if framecounter == 1 then
a = a .. "\n"
end
framecounter = framecounter + 1
a = "" -- 定義したもん!!!
end
(例とはいえど、こりゃすげぇクソコードだな)
attempt to index a nil value
local a = b[1]
nil
が代入されている変数を、テーブルとして読み取ろうとした場合に発生する。
上記の例では、変数b
が存在しない(未定義ornil
代入)のに(テーブルとして)要素を取り出そうとしている。
attempt to perform arithmetic on a nil value
local a = 1
a = a + b
nil
が代入されている変数を、計算しようとした場合に発生する。
上記の例では、変数b
が定義されていないのに、変数a
と計算しようとしている。
attempt to call a nil value
Hoge()
nil
が代入されている変数を、呼び出そうした場合に発生する
上記の例では、関数(変数)Hoge
を定義していない。
定義していても、書いている場所が定義が下にあると呼び出されない可能性がある。(local
使っていればなおさら)
'end' expected near '<eof>'
if true then
if true then
end
endが足りない場合に発生する。
cannot convert a 型名 to a clr type 型名
Player.Hurt("100")
型を間違えている。
上記の例では、Player.Hurt()
の引数は数値型(number)であるのに文字列型(string)を代入している。
unexpected symbol near '何か'
local fakePlayerData = {
LV = 1,
HP = 20
MasHP = 20,
}
''内にある単語の近くに何か文法がおかしいものがあることを表す。
上記では、unexpected symbol near 'MasHP'
というエラーメッセージであり、コンマが足りていない。
(正しくはHP = 20,
としなければならない。)
bad argument #n to 'メソッド名?' (型名1 expected, got 型名2)
local t = {93874, 3657896, 348950}
local t2 = {}
for i = 1, 3 do
t2[i] = math.pow(t, 2) -- 本来は t[i] のところが t になっている
end
-- bad argument #1 to 'pow' (number expected, got table)
標準ライブラリにあるメソッドの呼び出しで、型名が違う場合に発生する。
この例では、bad argument #1 to 'pow' (number expected, got table)
と表示され、
number
が入るところにtable
が入っているぞ と言う意味。
attempt to perform arithmetic on a string value
local t = "hoge"
t = t + t
文字列を+
で足し算しよう(繋ごう)とした場合に発生する。
文字列の連結は..
を使いましょう。
cannot access field メンバ名 of userdata<クラス名>
Misc.SakeScreen()
クラス名 に メンバ名 という 変数/関数(正しくはメンバだがここでは分かりやすく)は存在しないことを表す。
上記の例ではMisc.ShakeScreen()
を呼び出すつもりがスペルミスをしている。
ドキュメントで確認しよう!
Encounterスクリプト関連
敵の攻撃時間を自由にしたい(変えたい) / wavetimer
をいちいち設定するのがめんどくさい
個人的には攻撃ごとに時間が異なる方が作りやすい。
-- Encounterスクリプト内
wavetimer = math.huge
上記のように設定する場合は、Waveスクリプトで手動でEndWave()
を呼び出す必要があるので注意。
行数が多くなりすぎてとても読みずらい...
無理矢理ファイルで分けることが可能。
📁 Lua
├─ Encounters
│ └─ encounter.lua
├─ EncounterScripts
│ ├─ MaskMethods.lua
│ └─ FakePlayer.lua
├─ Monsters (割愛)
└─ Waves (割愛)
上記の例で、EncounterScripts
内にあるファイルはファイル最終行にreturn
さえあれば問題ない。
通常のEncounterスクリプトとして書く。
そしてEncounters/encounter.luaで
-- ファイルのどこか(関数外が良い)
require "EncounterScripts/MaskMethods"
require "EncounterScripts/FakePlayer"
で読み込む。
Monsterスクリプト関連
攻撃しても常にMISSにしたい
敵の防御力を強くすれば自動的にMISSになる。(計算して0ダメージの場合はMISSと表示されるようになる)
-- Monsterスクリプト内
def = 1023 -- このくらいあれば基本的に問題はないと思う。
def = 2147483647 -- Moonsharpでは数値(number)はdouble型だが... [要検証]
def = math.huge -- [要検証] バグりそう
だがしかし、今ではこっちの方を推奨する。
-- Monsterスクリプト内
function BeforeDamageCalculation()
SetDamage(0) -- 強制的にダメージを上書きする関数 0 を指定すべし
end
敵が避けるようにしたい
頑張れ。
原作のように一定時間経過したら攻撃(wave)に移行したい
結構簡単だった。
-- Monsterスクリプト内なら
currentdialogue = {"(内容)[w:60][next]"}
w
テキストコマンドだが、60
ならその4倍の240フレーム
(=4秒
)となるので、自分の好きな長さを設定できる。
ちなみにCYFはしゃべっている途中でもZキー
を押して先に進める(っぽい)。
発射体(Projectile)/弾丸(bullet)オブジェクト関連
x
とabsx
、y
とabsy
の違い。
x
やy
はアリーナ(ダイアログボックス)の中心を(0, 0)
とした座標。
absx
やabsy
は画面(ウインドウ)左下端を(0, 0)
とした座標。
アリーナ(Arena)(ダイアログボックス)内にあるときだけ表示したい
アリーナ内にあるときだけ表示したい場合は、empty
でスプライトオブジェクトを作成して、弾丸オブジェクトのスプライトを子オブジェクトにする。
sprite.Mask()
の使い方はスプライトオブジェクト関連
にある。
-- Waveスクリプト内
local bullets = {}
local box = CreateSprite("empty", "BelowBullet")
box.MoveToAbs(ArenaUtil.centerabsx, ArenaUtil.centerabsy)
box.Scale(Arena.width, Arena.height)
box.Mask("box")
function CreateBullet() --これはただ単に弾丸を生成する関数を定義しただけ
local bullet = CreateProjectile("bullet", Arena.width / 2 + 8, Player.y)
bullet.sprite.SetParent(box)
table.insert(bullets, bullet)
end
なお、親スプライトより先に弾丸オブジェクトを削除(Remove)しないと完全に削除されないバグが存在する。
上の例でいけば
function EndingWave()
-- 先に弾丸オブジェクトを削除
for i = 1, #bullets do
if bullets[i].isactive then
bullets[i].Remove()
end
end
-- 最後に親スプライトを削除
box.Remove()
end
としなければならない。
弾丸オブジェクトが生成されない
もし、sprite.Mask()
を使っているなら、ちょうど上に解決策がある。先に弾丸オブジェクトを削除すべし。
スプライト(Sprite)オブジェクト関連
Unityを使ってRemove()
確認
もしUnityを持っているなら、CYFのコードをダウンロードして、スプライトの削除確認ができる。
大きなプロジェクト(オーバーワールドなど)を作る際に役に立つ。
~Layer
というオブジェクトに子オブジェクトが存在するかしないかで
スプライトを全てRemove()
しているか確認できる。
スプライトを反転したい
sprite.Scale()
で負の値を代入すると反転する模様。
後で試すが、CYF作成者直々の回答なので間違いは無いだろう...
sprite.Mask()
の使い方
マスクをかけられる側 = 親スプライト(sprite.SetParent()
の()内に入れられる方)に、このMask()
を使う。
翻訳しても文が支離滅裂すぎて分からなかったので実際に聞いた。
local bullets = {}
local box = CreateSprite("empty", "BelowBullet")
box.MoveToAbs(ArenaUtil.centerabsx, ArenaUtil.centerabsy)
box.Scale(Arena.width, Arena.height)
box.Mask("box")
function CreateBullet()
local bullet = CreateProjectile("bullet", Arena.width / 2 + 8, Player.y)
bullet.sprite.SetParent(box)
table.insert(bullets, bullet)
end
いくつも違うサイズの長方形(正方形)を用意するのがめんどくさい/合計ファイルサイズが大きくなってしまう
CYFにはデフォルトでpx
というテクスチャがあるので、それを使うとよい。
Modフォルダに入れる必要は無い。
私がよく使う長方形(正方形)を生成する関数があるので君に贈ろう。
function NewSquare(x, y, width, height, color, layer)
if color == nil then color = {1, 1, 1} end
if layer == nil then layer = "BelowBullet" end
local px = CreateSprite("px", layer)
px.MoveToAbs(x, y)
px.color = color
px.Scale(width, height)
return px
end
ちなみに透明なものが欲しい場合はempty
を使用するとよい。
テキスト(text)オブジェクト関連
text.Move()
/text.SetVar()
/text.GetVar()
がエラー
そりゃエラーですよ、だって無いんだもん。
text.SetVar()
/text.GetVar()
の方は将来のアップデートで追加される可能性はある模様。
ちなみにtext.Move()
が無い理由を聞こうとしたが答えてはくれなかった。
使いづらい
私がめちゃくちゃ使う、お勧めの関数を君に贈ろう。
function NewText(text, x, y, layer)
if layer == nil then layer = "BelowBullet" end
local text = CreateText(text, {x, y}, 10000, layer)
text.HideBubble()
text.progressmode = "none"
return text
end
フォント関連
uibattlesmall
フォントで上手く表示されない
実はこのフォント、小文字対応していない。
しかもそのことは、ドキュメントに書かれていない。
日本語のフォントが使いたい
Determination JP をCYF用に作っている者がいるので調べるといいだろう。
え?公式のフォント?
私が既に作っている。AsteriskMod v0.5.3で公開する予定があるのでそこまで待っていてほしい。
フォントの作り方
フォントの作り方はドキュメントには載っていない。
次のサイトに載っている。
公式Disocrdチャンネル関連
質問したらRTFDというスタンプが来た
意味はRead The F***ing Documentation
、(ドキュメントを読め(、書いてあるだろ?))
という意味である。
翻訳かけて読めないことはないだろう...読もう!
その他
各色が知りたい
local BLACK = { 0, 0, 0 }
local WHITE = { 255, 255, 255 }
local RED = { 255, 0, 0 }
local ORANGE = { 255, 166, 0 }
local YELLOW = { 255, 255, 0 }
local GREEN = { 0, 192, 0 }
local CYAN = { 66, 226, 255 }
local BLUE = { 0, 60, 255 }
local PURPLE = { 213, 53, 217 }
水色(シアン色)のみアップデートで色が変化している
local CYAN_ALT = { 66, 226, 255 }
16進数の方はドキュメントのText commands
に載っている
タイトルやメニューが作りたい
開発者側としてはAsteris(略)をお勧めするが、CYFでの常套手段としてあるので紹介しよう。
簡単だ。Waveスクリプトで、戦闘画面自体を隠して、その上にスプライトやテキストを生成する。
それだけ。
-- Waveスクリプト
local mask = CreateSprite("px", "Top")
mask.color = {0, 0, 0}
mask.Scale(640, 480)
-- スプライトやテキストを生成
function Update()
-- ...
end
function EndingWave()
-- スプライトやテキストを削除
mask.Remove()
end
KRを実装したい
頑張れ。
...。
おっと、AsteriskModというのがあれば実は簡単に作れるんだぞ。そこにKRの例もある。
青色ソウルを実装したい
私が既に作っている。AsteriskMod v0.5.2.9で公開する予定があr(略)
実際、他の人が作っているものもある。