[HOME](https://hackmd.io/2BHmUlxZRy-eNbUEF2Mc-A?view) # インゲーム設計 ## クラス ### ボツ案 ```cpp= class StageManager { private: NoteManager noteManager; Player player; MusicData musicData; public: void loadFromFile(filePath) { // 譜面、メタデータファイルから情報取得 noteManager.loadNotesFromCSV(CSV文字列) } void update() { noteManager.update(); player.update(); } void draw() const { noteManager.draw(); player.sraw(); } } class NoteManager { private: Array<Note*> notes; public: void loadNotesFromCSV(String rawCSV) { // csv文字列からノーツ読み込み } void update() { for (auto note : notes) { note->update(); } } void draw() const { for (auto note : notes) { note->draw(); } } } class Player { public: void animate(PlayerAnimation); void update(); void drawAt(Vec2 pos); } class MusicData { public: double bpm; int beatPerMeasure; Duration firstBeatTimePos } ``` ## 設計(決定) ```mermaid classDiagram InGame "1" --> "1" Notes Notes "1" --> "0..n" Note Note <|-- それぞれのNote class InGame { -Array<Note*> notes -Audio music +update() +draw() -judge(now): Judgment } class Notes { -Array<Note*> notes +notesInsideScreen(now): Array } class Note { +draw() } ``` ```cpp class Notes { Array <std::shared_ptr<Note>> notes; Init() { // 譜面読み込み while() { notes << std::make_shared<Node>(args...); } } notesInsideScreen(now) { return notes.filter([](Note *n){ return !n->isOutOfRange(now); }); } } class InGame { Notes notes = {...}; Audio music; InGame::update() { inputType = ... now = music.posSec(); player.update(); effectManager.update(); for (auto note : notes.notesInsideScreen(now)) { note.update(); // animation judge.judge(now, note, inputType); } } InGame::draw() { now = 今の時間 for (auto note : notes.notesInsideScreen(now)) { note.draw(); } } const double margin = 0.1; Judgment judge(now, note, inputType) { // 画面に入ってきてすぐのノーツに反応してしまう // ジャッジはプレイヤーの一定範囲内でのみ行う if (!note->isActive()) return Judgment::None; if (note->type != inputType) return Judgment::None; if (abs(now - note->time) < margin) { return Judgment::Success; } else { return Judgment::failure; } } } class Note { protected: Note(Duration timePos, double intervalBeat) : timePos(timePos), intervalBeat(intervalBeat) {} virtual ~Note() {} // 仮想デストラクタ const Duration timePos; // ノーツの配置位置(s) const double intervalBeat; // ノーツが出現してからプレイヤーに衝突するまでの拍数 NoteState state; // ノーツの状態(回避に成功したかどうか) AvoidType avoidType; // 回避の仕方 public: virtual void update(Duration now, Player player) = 0; virtual void draw(Duration now, Player player) const = 0; virtual bool isActive() const = 0; void setState(NoteState nextState) { this->state = nextState; } bool isOutOfRange(double now) { } } ``` Note::update()で何をするか? InGameシーンがすべてのノーツと曲をもち、画面内ノーツを対象に処理・判定を行う。 シーンがJudgeしてもいいよね? ### Animation AnimationTexture.h ```cpp class AnimationTexture { private: Texture texture; // 元となる画像 Point frameSize; // 1フレームのサイズ(ピクセル) int numFrame; // フレーム数 public: AnimationTexture(Texture texture, Point frameSize); // 指定したフレーム番号のテクスチャを返す Texture frame(int frame); // switchSpanの期間ごとにフレームを切り替えたテクスチャを返す // timePosの剰余を取ってアニメーションさせる // (timePos / switchSpan) mod numFrame Texture animateByTime(Duration timePos, Duration switchSpan); } ``` AnimationTexture.cpp ```cpp AnimationTexture::AnimationTexture(Texture texture, Point frameSize, int numFrame) : texture(texture), frameSize(frameSize), numFrame(numFrame) {} Texture AnimationTexture::draw(int frame) { // 実装 } Texture AnimationTexture::animateByTime(Duration timePos, Duration switchSpan) { // 実装 } ``` ### UV管理(仮) `playerJump.png` 画像。 `playerJump.json` UVをずらすために1フレームの横と縦の長さを指定する。 ```json { "widthPerFrame": 10, "heightPerFrame": 20 } ``` CSVでもいいかもー `playerJump.csv` ```csv 10,20 ``` ### アセット管理 Asset.h ```cpp= namespace Asset { namespace Texture { String playerRun{U"playerRun.png"}; String playerJump{U"playerJump.png"}; } } ``` Asset.cpp ```cpp= void RegisterAsset() { } ```