-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathhold_note.cpp
More file actions
149 lines (123 loc) · 4.8 KB
/
Copy pathhold_note.cpp
File metadata and controls
149 lines (123 loc) · 4.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#include "hold_note.h"
#include "game.h"
#include <cmath>
// ======================================================
// 子ノートの間隔(拍数)― ここを変えると密度が変わる
// 0.25f = 1/4拍ごと(デフォルト)
// 0.125f = 1/8拍ごと(より細かい)
// 0.5f = 1/2拍ごと(より荒い)
// ======================================================
static const float HOLD_BEAT_INTERVAL = 0.25f;
// NoteManager 側の定数と揃えること
static const float HOLD_HIT_ZONE_Z = 3.0f;
static const float HOLD_HIT_WINDOW = 2.3f;
// 子ノートの大きさ(EnemyNote::Init() で設定される基準スケールに対する倍率)
static const float HOLD_FIRST_CHILD_SCALE_MULT = 1.1f; // 一体目は少し大きく
static const float HOLD_CHILD_SCALE_MULT = 0.8f; // それ以外は少し小さく
// ホールドGargoyleのリム色。先頭と後続で個別に変更できる。
static const XMFLOAT3 HOLD_FIRST_RIM_COLOR = { 1.0f, 0.15f, 0.08f };
static const XMFLOAT3 HOLD_CHILD_RIM_COLOR = { 1.0f, 1.0f, 1.0f };
static const float HOLD_RIM_INTENSITY = 0.50f;
HoldNote::~HoldNote()
{
for (EnemyNote* child : m_ChildNotes)
delete child;
m_ChildNotes.clear();
}
void HoldNote::Init(int lane, int endLane, int face, float initZ, float endZ, float speed, float bpm, float beat, float endBeat)
{
m_Face = face;
m_Speed = speed;
m_IsActive = true;
m_IsHit = false;
// JSON lane (0=左, 1=中央, 2=右) → ゲーム lane (-1=左, 0=中央, 1=右) に変換
int gameLane = lane - 1;
int gameEndLane = endLane - 1;
// 1子ノートあたりのZ間隔 = beat間隔(拍) × (60秒/bpm) × speed
float zStep = HOLD_BEAT_INTERVAL * (60.0f / bpm) * speed;
// 始点〜終点を拍数ベースで分割して子ノート数を決定(Z座標ベースだと同期ズレの誤差影響を受けるため)
float diffBeat = endBeat - beat;
float stepVal = diffBeat / HOLD_BEAT_INTERVAL;
int totalSteps = (stepVal > 0.0f) ? (int)roundf(stepVal) + 1 : 2;
if (totalSteps < 2) totalSteps = 2;
for (int i = 0; i < totalSteps; i++)
{
float childBeat = beat + (float)i * HOLD_BEAT_INTERVAL;
// 譜面の endBeat を超えた場合はそれ以降の生成を打ち切る
if (childBeat > endBeat + 0.001f) break;
// t: 0.0(始点gameLane)〜 1.0(終点gameEndLane)の進捗
float t = (float)i / (float)(totalSteps - 1);
// 理想レーンを線形補間し、最寄りレーン(-1〜1)にスナップ
float idealLane = gameLane + t * (gameEndLane - gameLane);
int snapLane = (int)roundf(idealLane);
snapLane = (snapLane < -1) ? -1 : (snapLane > 1) ? 1 : snapLane;
float childZ = initZ + (float)i * zStep;
EnemyNote* child = new EnemyNote();
// 一体目も共通モデルを使用し、テクスチャのみを差し替える
child->Init(snapLane, face, childZ, speed, nullptr);
child->SetBeat(childBeat);
if (i == 0)
{
child->SetCustomTexture("asset/texture/Gargoyle_red.png");
child->SetRimLightColor(HOLD_FIRST_RIM_COLOR);
}
else
{
child->SetCustomTexture("asset/texture/Gargoyle_white.png");
child->SetRimLightColor(HOLD_CHILD_RIM_COLOR);
}
child->SetRimLightIntensity(HOLD_RIM_INTENSITY);
// 一体目だけ少し大きく、それ以外は少し小さく表示する
float scaleMult = (i == 0) ? HOLD_FIRST_CHILD_SCALE_MULT : HOLD_CHILD_SCALE_MULT;
XMFLOAT3 baseScale = child->GetScale();
child->SetSize({ baseScale.x * scaleMult, baseScale.y * scaleMult, baseScale.z * scaleMult });
m_ChildNotes.push_back(child);
}
}
void HoldNote::Update()
{
bool anyActive = false;
for (EnemyNote* child : m_ChildNotes)
{
if (!child->IsActive()) continue;
child->Update();
// 判定窓を通過したら押し逃しMiss
// プレイヤーと同じlane/faceにいた場合のみダメージあり、それ以外はコンボリセットのみ
if (!child->IsHit() && child->GetPosZ() < HOLD_HIT_ZONE_Z - HOLD_HIT_WINDOW)
{
bool isDamage = (child->GetLaneIndex() == m_PlayerLane && child->GetFace() == m_PlayerFace);
child->OnMiss();
m_PendingMissJudges.push(isDamage);
}
if (child->IsActive())
anyActive = true;
}
// 全子ノートが非アクティブになったら自身も非アクティブ
if (!anyActive)
m_IsActive = false;
}
void HoldNote::Draw()
{
for (EnemyNote* child : m_ChildNotes)
{
if (child->IsActive())
child->Draw();
}
}
EnemyNote* HoldNote::GetNearestActiveChild(int lane, int face) const
{
EnemyNote* nearest = nullptr;
float minDist = FLT_MAX;
for (EnemyNote* child : m_ChildNotes)
{
if (!child->IsActive() || child->IsHit()) continue;
if (!IsSameOrCornerPosition(child->GetLaneIndex(), child->GetFace(), lane, face)) continue;
float dist = fabsf(child->GetPosZ() - HOLD_HIT_ZONE_Z);
if (dist < minDist)
{
minDist = dist;
nearest = child;
}
}
return nearest;
}