-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathCharacterController.cpp
More file actions
340 lines (285 loc) · 10.5 KB
/
Copy pathCharacterController.cpp
File metadata and controls
340 lines (285 loc) · 10.5 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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
#include "CharacterController.h"
#include "Core.Memory.hpp"
#include "PhysicsMathAdapter.h"
#include <iostream>
CharacterController::CharacterController()
{
}
CharacterController::~CharacterController()
{
CollisionData* data = (CollisionData*)m_controller->getActor()->userData;
data->isDead = true;
// physics update에서 지연 삭제
/*if (m_controller)
{
m_controller->release();
m_controller = nullptr;
}*/
//터지니? 만약에 중앙 통제면, 메니저에서 할당하고 넘겨줘야지...
Memory::SafeDelete(m_characterMovement);
Memory::SafeDelete(m_filters);
Memory::SafeDelete(m_filterData);
}
void CharacterController::Initialize(const CharacterControllerInfo& info, const CharacterMovementInfo& moveInfo, physx::PxControllerManager* CCTManager, physx::PxMaterial* material, CollisionData* collisionData, unsigned int* collisionMatrix, std::function<void(CollisionData, ECollisionEventType)> callback)
{
m_id = info.id;
m_layerNumber = info.layerNumber;
m_material = material;
m_isForcedMoveActive = false;
//케릭터 충돌 필터 설정
m_filterData = new physx::PxFilterData();
m_filterData->word0 = m_layerNumber; //layer number
m_filterData->word1 = collisionMatrix[m_layerNumber];
m_filterData->word2 = 1 << m_layerNumber; // layer bitmask
m_filters = new physx::PxControllerFilters(m_filterData);
m_filters->mFilterCallback = new PhysicsControllerFilterCallback(m_layerNumber, collisionMatrix); //&&&&&filter
m_filters->mCCTFilterCallback = new CCTFilterCallback();
m_characterMovement = new CharacterMovement();
m_characterMovement->Initialize(moveInfo);
m_hitReportCallback = new PhysicsControllerHitReport(callback);
}
math::vector3 CharacterController::GetPosition() const
{
return PhysicsMath::FromPx(m_controller->getPosition());
}
void CharacterController::SetPosition(const math::vector3& position)
{
m_controller->setPosition(PhysicsMath::ToPxExtended(position));
}
void CharacterController::Update(float deltaTime)
{
// 이번 프레임에 적용될 최종 이동 변위 벡터
physx::PxVec3 currentFrameVelocity(0.f, 0.f, 0.f);
// 1. 상태에 따라 이번 프레임의 이동 방향 결정
// 1. 강제 이동 상태일 경우
if (m_isForcedMoveActive)
{
if (m_forcedMoveTotalDuration <= 0.f) // 지속적인 힘
{
m_forcedMoveCurrentVelocity = m_forcedMoveInitialVelocity;
}
else // 시간이 정해진 움직임
{
m_forcedMoveTimer -= deltaTime;
if (m_forcedMoveTimer <= 0.f)
{
StopForcedMove();
}
}
m_forcedMoveCurrentVelocity.y -= m_gravityWeight * deltaTime;
currentFrameVelocity = PhysicsMath::ToPx(m_forcedMoveCurrentVelocity);
}
else
{
// 일반 이동 계산
m_characterMovement->Update(deltaTime, m_inputMove, m_IsDynamic);
currentFrameVelocity = PhysicsMath::ToPx(m_characterMovement->GetOutVector());
// 일반 이동이 끝났으므로 입력 초기화
m_inputMove = {};
m_IsDynamic = false;
}
// 2. 이동 제한 적용 (공통 로직)
// 강제 이동이든 일반 이동이든 상관없이 항상 이동 제한을 체크
if (currentFrameVelocity.x < 0.0f && m_bMoveRestrict[static_cast<int>(ERestrictDirection::MINUS_X)])
{
currentFrameVelocity.x = 0.0f;
}
else if (currentFrameVelocity.x > 0.0f && m_bMoveRestrict[static_cast<int>(ERestrictDirection::PlUS_X)])
{
currentFrameVelocity.x = 0.0f;
}
if (currentFrameVelocity.z < 0.0f && m_bMoveRestrict[static_cast<int>(ERestrictDirection::MINUS_Z)])
{
currentFrameVelocity.z = 0.0f;
}
else if (currentFrameVelocity.z > 0.0f && m_bMoveRestrict[static_cast<int>(ERestrictDirection::PLUS_Z)])
{
currentFrameVelocity.z = 0.0f;
}
// 3. 컨트롤러 이동 실행 (공통 로직 - move()는 여기서 딱 한 번만!)
physx::PxControllerCollisionFlags collisionFlag = m_controller->move(currentFrameVelocity, 0.01f, deltaTime, *m_filters);
// 4. 이동 후 처리 (공통 로직)
if (m_hitReportCallback)
{
m_hitReportCallback->UpdateAndDispatchEndEvents();
}
// 바닥면 충돌 체크
if (collisionFlag & physx::PxControllerCollisionFlag::eCOLLISION_DOWN) {
m_characterMovement->SetIsFall(false);
}
else
{
m_characterMovement->SetIsFall(true);
}
}
void CharacterController::AddMovementInput(const math::vector3& input, bool isDynamic)
{
if (std::abs(input.x)>0)
{
m_inputMove.x = input.x;
}
if (std::abs(input.y) > 0)
{
m_inputMove.y = input.y;
}
if (std::abs(input.z) > 0)
{
m_inputMove.z = input.z;
}
m_IsDynamic = isDynamic;
}
bool CharacterController::ChangeLayerNumber(const unsigned int& newLayerNumber, unsigned int* collisionMatrix)
{
if (newLayerNumber == UINT_MAX)
{
return false;
}
m_layerNumber = newLayerNumber;
//physx::PxFilterData filterData;
m_filterData->word0 = m_layerNumber;
m_filterData->word1 = collisionMatrix[m_layerNumber];
m_filterData->word2 = 1 << m_layerNumber;
PxShape* shape = nullptr;
m_controller->getActor()->getShapes(&shape, 1);
if (shape != nullptr) {
shape->setQueryFilterData(*m_filterData);
shape->setSimulationFilterData(*m_filterData);
}
static_cast<PhysicsControllerFilterCallback*>(m_filters->mFilterCallback)->SetCharacterLayer(m_layerNumber);
//
}
void CharacterController::StartForcedMove(const math::vector3& initialVelocity, float duration)
{
m_isForcedMoveActive = true;
m_forcedMoveTimer = duration;
m_forcedMoveTotalDuration = duration;
m_forcedMoveInitialVelocity = initialVelocity;
m_forcedMoveCurrentVelocity = initialVelocity;
}
//void CharacterController::StartForcedMove(const math::vector3& initialVelocity, float duration)
//{
// m_isForcedMoveActive = true;
// m_forcedMoveTimer = duration;
// m_forcedMoveTotalDuration = duration;
// m_forcedMoveInitialVelocity = initialVelocity;
//}
void CharacterController::StopForcedMove()
{
m_isForcedMoveActive = false;
m_forcedMoveTimer = 0.f;
m_forcedMoveTotalDuration = 0.f;
m_forcedMoveInitialVelocity = math::vector3{};
m_forcedMoveCurrentVelocity = math::vector3{};
}
bool CharacterController::IsInForcedMove() const
{
return m_isForcedMoveActive;
}
PhysicsControllerHitReport::PhysicsControllerHitReport(std::function<void(const CollisionData&, ECollisionEventType)> callback)
: m_controller(nullptr), m_callbackFunction(callback)
{
}
PhysicsControllerHitReport::~PhysicsControllerHitReport()
{
}
void PhysicsControllerHitReport::onShapeHit(const PxControllerShapeHit& hit)
{
//collider
CollisionData* controllerData = (CollisionData*)hit.controller->getActor()->userData;
CollisionData* shapeData = (CollisionData*)hit.actor->userData;
if (controllerData == nullptr || shapeData == nullptr || m_callbackFunction == nullptr) return;
m_currentContacts.insert(hit.actor);
ECollisionEventType eventType;
if (m_previousContacts.find(hit.actor) == m_previousContacts.end())
{
eventType = ECollisionEventType::ENTER_COLLISION;
}
else
{
eventType = ECollisionEventType::ON_COLLISION;
}
std::vector<math::vector3> contactPoints;
const math::vector3 contactPoint = PhysicsMath::FromPx(hit.worldPos); // PhysX 벡터를 엔진 벡터로 변환
contactPoints.push_back(contactPoint); // 벡터에 추가
CollisionData firstActor;
firstActor.thisId = controllerData->thisId;
firstActor.otherId = shapeData->thisId;
firstActor.thisLayerNumber = controllerData->thisLayerNumber;
firstActor.otherLayerNumber = shapeData->thisLayerNumber;
firstActor.contactPoints = contactPoints;
CollisionData secondActor;
secondActor.thisId = shapeData->thisId;
secondActor.otherId = controllerData->thisId;
secondActor.thisLayerNumber = shapeData->thisLayerNumber;
secondActor.otherLayerNumber = controllerData->thisLayerNumber;
secondActor.contactPoints = contactPoints;
//std::cout << "PhysicsControllerHitReport::onShapeHit - thisId: " << firstActor.thisId << ", otherId: " << firstActor.otherId << ", EventType: " << static_cast<int>(eventType) << std::endl;
m_callbackFunction(firstActor, eventType);
m_callbackFunction(secondActor, eventType);
}
void PhysicsControllerHitReport::onControllerHit(const PxControllersHit& hit)
{
// CCT와 CCT의 충돌을 처리합니다.
CollisionData* controllerData = (CollisionData*)hit.controller->getActor()->userData;
CollisionData* otherControllerData = (CollisionData*)hit.other->getActor()->userData;
if (controllerData == nullptr || otherControllerData == nullptr || m_callbackFunction == nullptr) return;
// 현재 충돌한 액터(상대방 CCT)를 목록에 추가합니다.
m_currentContacts.insert(hit.other->getActor());
ECollisionEventType eventType;
// 이전 프레임에 충돌 목록에 없었다면 ENTER
if (m_previousContacts.find(hit.other->getActor()) == m_previousContacts.end())
{
eventType = ECollisionEventType::ENTER_COLLISION;
}
else // 있었다면 STAY
{
eventType = ECollisionEventType::ON_COLLISION;
}
// [수정] CCT 간 충돌에서도 충돌 지점 정보를 추가합니다.
std::vector<math::vector3> contactPoints;
const math::vector3 contactPoint = PhysicsMath::FromPx(hit.worldPos);
contactPoints.push_back(contactPoint);
// CollisionData를 생성하고 정보를 채웁니다.
CollisionData firstActor;
firstActor.thisId = controllerData->thisId;
firstActor.otherId = otherControllerData->thisId;
firstActor.thisLayerNumber = controllerData->thisLayerNumber;
firstActor.otherLayerNumber = otherControllerData->thisLayerNumber;
firstActor.contactPoints = contactPoints; // 할당
CollisionData secondActor;
secondActor.thisId = otherControllerData->thisId;
secondActor.otherId = controllerData->thisId;
secondActor.thisLayerNumber = otherControllerData->thisLayerNumber;
secondActor.otherLayerNumber = controllerData->thisLayerNumber;
secondActor.contactPoints = contactPoints; // 할당
m_callbackFunction(firstActor, eventType);
m_callbackFunction(secondActor, eventType);
}
void PhysicsControllerHitReport::onObstacleHit(const PxControllerObstacleHit& hit)
{
}
void PhysicsControllerHitReport::UpdateAndDispatchEndEvents()
{
if (!m_controller) return;
for (auto* actor : m_previousContacts)
{
// END_COLLISION ´ ´ ´
// ¹
if (m_currentContacts.find(actor) == m_currentContacts.end())
{
CollisionData* controllerData = (CollisionData*)m_controller->getActor()->userData;
CollisionData* shapeData = (CollisionData*)actor->userData;
if (controllerData == nullptr || shapeData == nullptr || m_callbackFunction == nullptr) continue;
CollisionData firstActor, secondActor;
firstActor.thisId = controllerData->thisId;
firstActor.otherId = shapeData->thisId;
secondActor.thisId = shapeData->thisId;
secondActor.otherId = controllerData->thisId;
m_callbackFunction(firstActor, ECollisionEventType::END_COLLISION);
m_callbackFunction(secondActor, ECollisionEventType::END_COLLISION);
}
}
m_previousContacts = m_currentContacts;
//std::cout << "PhysicsControllerHitReport::UpdateAndDispatchEndEvents - Current Contacts Size: " << m_currentContacts.size() << std::endl;
m_currentContacts.clear();
}