FT collision part4: ball to ball collision.

TBall uses multiple inheritance, interesting.
This commit is contained in:
Muzychenko Andrey 2023-03-12 11:12:41 +03:00
parent f521a03322
commit c5acdcd524
15 changed files with 132 additions and 38 deletions

View File

@ -11,7 +11,8 @@
#include "TPinballTable.h" #include "TPinballTable.h"
#include "TTableLayer.h" #include "TTableLayer.h"
TBall::TBall(TPinballTable* table) : TPinballComponent(table, -1, false) TBall::TBall(TPinballTable* table, int groupIndex) : TCollisionComponent(table, groupIndex, false),
TEdgeSegment(this, &ActiveFlag, 0)
{ {
visualStruct visual{}; visualStruct visual{};
char ballGroupName[10]{"ball"}; char ballGroupName[10]{"ball"};
@ -22,19 +23,29 @@ TBall::TBall(TPinballTable* table) : TPinballComponent(table, -1, false)
CollisionComp = nullptr; CollisionComp = nullptr;
EdgeCollisionCount = 0; EdgeCollisionCount = 0;
TimeDelta = 0.0; TimeDelta = 0.0;
CollisionMask = 1;
CollisionFlag = 0; CollisionFlag = 0;
Speed = 0.0; Speed = 0.0;
Direction.Y = 0.0; Direction.Y = 0.0;
Direction.X = 0.0; Direction.X = 0.0;
Position.X = 0.0;
Position.Y = 0.0;
HasGroupFlag = false;
ListBitmap = new std::vector<SpriteData>(); ListBitmap = new std::vector<SpriteData>();
if (groupIndex == -1)
{
HasGroupFlag = false;
Position = {0, 0, 0};
CollisionMask = 1;
}
else
{
HasGroupFlag = true;
loader::query_visual(groupIndex, 0, &visual);
CollisionMask = visual.CollisionGroup;
auto floatArr = loader::query_float_attribute(groupIndex, 0, 408);
Position = {floatArr[0], floatArr[1], floatArr[3]};
}
/*Full tilt: ball is ballN, where N[0,2] resolution*/ /*Full tilt: ball is ballN, where N[0,2] resolution*/
auto groupIndex = loader::query_handle(ballGroupName); groupIndex = loader::query_handle(ballGroupName);
if (groupIndex < 0) if (groupIndex < 0)
{ {
ballGroupName[4] = '0' + fullscrn::GetResolution(); ballGroupName[4] = '0' + fullscrn::GetResolution();
@ -70,7 +81,7 @@ void TBall::Repaint()
auto pos2D = proj::xform_to_2d(Position); auto pos2D = proj::xform_to_2d(Position);
auto zDepth = proj::z_distance(Position); auto zDepth = proj::z_distance(Position);
auto index = 0u; auto index = 0u;
for (; index < ListBitmap->size() - 1; ++index) for (; index < ListBitmap->size() - 1; ++index)
{ {
@ -112,7 +123,7 @@ int TBall::Message(MessageCode code, float value)
{ {
if (code == MessageCode::Reset) if (code == MessageCode::Reset)
{ {
SpriteSetBall(-1, { 0,0 }, 0.0f); SpriteSetBall(-1, {0, 0}, 0.0f);
Position.X = 0.0; Position.X = 0.0;
CollisionComp = nullptr; CollisionComp = nullptr;
Position.Y = 0.0; Position.Y = 0.0;
@ -139,6 +150,47 @@ void TBall::throw_ball(vector3* direction, float angleMult, float speedMult1, fl
Speed = (1.0f - (rnd + rnd)) * (speedMult1 * speedMult2) + speedMult1; Speed = (1.0f - (rnd + rnd)) * (speedMult1 * speedMult2) + speedMult1;
} }
void TBall::EdgeCollision(TBall* ball, float distance)
{
ball->AsEdgeCollisionFlag = true;
vector2 nextPos{
ball->Position.X + ball->Direction.X * distance,
ball->Position.Y + ball->Direction.Y * distance
},
collDir{nextPos.X - Position.X, nextPos.Y - Position.Y};
maths::normalize_2d(collDir);
vector2 invCollDir{ -collDir.X, -collDir.Y };
ball->Position.X = nextPos.X;
ball->Position.Y = nextPos.Y;
ball->Direction.X *= ball->Speed;
ball->Direction.Y *= ball->Speed;
Direction.X *= Speed;
Direction.Y *= Speed;
auto coef = -maths::DotProduct(ball->Direction, collDir);
vector2 v44{collDir.X * coef, collDir.Y * coef};
vector2 v13{ball->Direction.X + v44.X, ball->Direction.Y + v44.Y};
coef = -maths::DotProduct(Direction, invCollDir);
vector2 v11{invCollDir.X * coef, invCollDir.Y * coef};
vector2 v10{Direction.X + v11.X, Direction.Y + v11.Y};
ball->Direction.X = -v11.X + v13.X;
ball->Direction.Y = -v11.Y + v13.Y;
ball->Speed = maths::normalize_2d(ball->Direction);
Direction.X = -v44.X + v10.X;
Direction.Y = -v44.Y + v10.Y;
Speed = maths::normalize_2d(Direction);
}
float TBall::FindCollisionDistance(const ray_type& ray)
{
// Original inherits TCircle and aliases position.
const circle_type ballCircle{{Position.X, Position.Y}, Radius * Radius * 4.0f};
return maths::ray_intersect_circle(ray, ballCircle);
}
vector2 TBall::get_coordinates() vector2 TBall::get_coordinates()
{ {
return TTableLayer::edge_manager->NormalizeBox(Position); return TTableLayer::edge_manager->NormalizeBox(Position);

View File

@ -1,14 +1,12 @@
#pragma once #pragma once
#include "maths.h" #include "maths.h"
#include "TPinballComponent.h" #include "TCollisionComponent.h"
#include "TEdgeSegment.h"
class TCollisionComponent; class TBall : public TCollisionComponent, public TEdgeSegment
class TEdgeSegment;
class TBall : public TPinballComponent
{ {
public : public :
TBall(TPinballTable* table); TBall(TPinballTable* table, int groupIndex);
void Repaint(); void Repaint();
void not_again(TEdgeSegment* edge); void not_again(TEdgeSegment* edge);
bool already_hit(TEdgeSegment* edge); bool already_hit(TEdgeSegment* edge);
@ -16,6 +14,9 @@ public :
vector2 get_coordinates() override; vector2 get_coordinates() override;
void Disable(); void Disable();
void throw_ball(vector3* direction, float angleMult, float speedMult1, float speedMult2); void throw_ball(vector3* direction, float angleMult, float speedMult1, float speedMult2);
void place_in_grid(RectF* aabb) override {}
void EdgeCollision(TBall* ball, float distance) override;
float FindCollisionDistance(const ray_type& ray) override;
vector3 Position{}; vector3 Position{};
vector3 PrevPosition{}; vector3 PrevPosition{};

View File

@ -12,9 +12,9 @@ TCircle::TCircle(TCollisionComponent* collComp, char* activeFlag, unsigned colli
Circle.Center = *center; Circle.Center = *center;
} }
float TCircle::FindCollisionDistance(ray_type* ray) float TCircle::FindCollisionDistance(const ray_type& ray)
{ {
return maths::ray_intersect_circle(*ray, Circle); return maths::ray_intersect_circle(ray, Circle);
} }
void TCircle::EdgeCollision(TBall* ball, float distance) void TCircle::EdgeCollision(TBall* ball, float distance)

View File

@ -10,7 +10,7 @@ public:
TCircle(TCollisionComponent* collComp, char* activeFlag, unsigned int collisionGroup, vector2* center, TCircle(TCollisionComponent* collComp, char* activeFlag, unsigned int collisionGroup, vector2* center,
float radius); float radius);
float FindCollisionDistance(ray_type* ray) override; float FindCollisionDistance(const ray_type& ray) override;
void EdgeCollision(TBall* ball, float distance) override; void EdgeCollision(TBall* ball, float distance) override;
void place_in_grid(RectF* aabb) override; void place_in_grid(RectF* aabb) override;
}; };

View File

@ -76,7 +76,7 @@ int TEdgeManager::TestGridBox(int x, int y, float* distPtr, TEdgeSegment** edgeD
for (auto it = edgeBox->EdgeList.rbegin(); it != edgeBox->EdgeList.rend(); ++it) for (auto it = edgeBox->EdgeList.rbegin(); it != edgeBox->EdgeList.rend(); ++it)
{ {
auto edge = *it; auto edge = *it;
if (!edge->ProcessedFlag && *edge->ActiveFlag && (edge->CollisionGroup & ray->CollisionMask)) if (!edge->ProcessedFlag && *edge->ActiveFlagPtr && (edge->CollisionGroup & ray->CollisionMask))
{ {
if (!ball->already_hit(edge)) if (!ball->already_hit(edge))
{ {
@ -84,7 +84,7 @@ int TEdgeManager::TestGridBox(int x, int y, float* distPtr, TEdgeSegment** edgeD
*edgePtr = edge; *edgePtr = edge;
++edgePtr; ++edgePtr;
edge->ProcessedFlag = 1; edge->ProcessedFlag = 1;
auto dist = edge->FindCollisionDistance(ray); auto dist = edge->FindCollisionDistance(*ray);
if (dist < *distPtr) if (dist < *distPtr)
{ {
*distPtr = dist; *distPtr = dist;

View File

@ -8,7 +8,7 @@
TEdgeSegment::TEdgeSegment(TCollisionComponent* collComp, char* activeFlag, unsigned collisionGroup) TEdgeSegment::TEdgeSegment(TCollisionComponent* collComp, char* activeFlag, unsigned collisionGroup)
{ {
CollisionComponent = collComp; CollisionComponent = collComp;
ActiveFlag = activeFlag; ActiveFlagPtr = activeFlag;
CollisionGroup = collisionGroup; CollisionGroup = collisionGroup;
ProcessedFlag = 0; ProcessedFlag = 0;
} }

View File

@ -15,7 +15,7 @@ class TEdgeSegment
{ {
public: public:
TCollisionComponent* CollisionComponent; TCollisionComponent* CollisionComponent;
char* ActiveFlag; char* ActiveFlagPtr;
char ProcessedFlag; char ProcessedFlag;
void* WallValue{}; void* WallValue{};
unsigned int CollisionGroup; unsigned int CollisionGroup;
@ -26,7 +26,7 @@ public:
virtual void EdgeCollision(TBall* ball, float distance) = 0; virtual void EdgeCollision(TBall* ball, float distance) = 0;
virtual void port_draw(); virtual void port_draw();
virtual void place_in_grid(RectF* aabb) = 0; virtual void place_in_grid(RectF* aabb) = 0;
virtual float FindCollisionDistance(ray_type* ray) = 0; virtual float FindCollisionDistance(const ray_type& ray) = 0;
static TEdgeSegment* install_wall(float* floatArr, TCollisionComponent* collComp, char* activeFlagPtr, static TEdgeSegment* install_wall(float* floatArr, TCollisionComponent* collComp, char* activeFlagPtr,
unsigned int collisionGroup, float offset, size_t wallValue); unsigned int collisionGroup, float offset, size_t wallValue);

View File

@ -104,12 +104,12 @@ void TFlipperEdge::port_draw()
set_control_points(CurrentAngle); set_control_points(CurrentAngle);
} }
float TFlipperEdge::FindCollisionDistance(ray_type* ray) float TFlipperEdge::FindCollisionDistance(const ray_type& ray)
{ {
ray_type dstRay{}; ray_type dstRay{};
if (ControlPointDirtyFlag) if (ControlPointDirtyFlag)
set_control_points(CurrentAngle); set_control_points(CurrentAngle);
auto distance = maths::distance_to_flipper(this, *ray, dstRay); auto distance = maths::distance_to_flipper(this, ray, dstRay);
if (distance >= 1e9f) if (distance >= 1e9f)
return 1e9f; return 1e9f;

View File

@ -12,7 +12,7 @@ public:
vector3* origin, vector3* vecT1, vector3* vecT2, float extendSpeed, float retractSpeed, float collMult, vector3* origin, vector3* vecT1, vector3* vecT2, float extendSpeed, float retractSpeed, float collMult,
float elasticity, float smoothness); float elasticity, float smoothness);
void port_draw() override; void port_draw() override;
float FindCollisionDistance(ray_type* ray) override; float FindCollisionDistance(const ray_type& ray) override;
void EdgeCollision(TBall* ball, float distance) override; void EdgeCollision(TBall* ball, float distance) override;
void place_in_grid(RectF* aabb) override; void place_in_grid(RectF* aabb) override;
void set_control_points(float angle); void set_control_points(float angle);

View File

@ -36,9 +36,9 @@ void TLine::Offset(float offset)
maths::line_init(Line, X0, Y0, X1, Y1); maths::line_init(Line, X0, Y0, X1, Y1);
} }
float TLine::FindCollisionDistance(ray_type* ray) float TLine::FindCollisionDistance(const ray_type& ray)
{ {
return maths::ray_intersect_line(*ray, Line); return maths::ray_intersect_line(ray, Line);
} }
void TLine::EdgeCollision(TBall* ball, float distance) void TLine::EdgeCollision(TBall* ball, float distance)

View File

@ -11,7 +11,7 @@ public:
TLine(TCollisionComponent* collCmp, char* activeFlag, unsigned int collisionGroup, float x0, float y0, float x1, float y1); TLine(TCollisionComponent* collCmp, char* activeFlag, unsigned int collisionGroup, float x0, float y0, float x1, float y1);
TLine(TCollisionComponent* collCmp, char* activeFlag, unsigned int collisionGroup, const vector2& start, const vector2& end); TLine(TCollisionComponent* collCmp, char* activeFlag, unsigned int collisionGroup, const vector2& start, const vector2& end);
void Offset(float offset); void Offset(float offset);
float FindCollisionDistance(ray_type* ray) override; float FindCollisionDistance(const ray_type& ray) override;
void EdgeCollision(TBall* ball, float distance) override; void EdgeCollision(TBall* ball, float distance) override;
void place_in_grid(RectF* aabb) override; void place_in_grid(RectF* aabb) override;
}; };

View File

@ -625,7 +625,7 @@ TBall* TPinballTable::AddBall(float x, float y)
{ {
if (BallList.size() >= 20) if (BallList.size() >= 20)
return nullptr; return nullptr;
ball = new TBall(this); ball = new TBall(this, -1);
BallList.push_back(ball); BallList.push_back(ball);
} }
@ -655,6 +655,16 @@ int TPinballTable::BallCountInRect(const RectF& rect)
return count; return count;
} }
int TPinballTable::BallCountInRect(const vector2& pos, float margin)
{
RectF rect{};
rect.XMin = pos.X - margin;
rect.XMax = pos.X + margin;
rect.YMin = pos.Y - margin;
rect.YMax = pos.Y + margin;
return BallCountInRect(rect);
}
void TPinballTable::EndGame_timeout(int timerId, void* caller) void TPinballTable::EndGame_timeout(int timerId, void* caller)
{ {
auto table = static_cast<TPinballTable*>(caller); auto table = static_cast<TPinballTable*>(caller);

View File

@ -37,6 +37,7 @@ public:
int Message(MessageCode code, float value) override; int Message(MessageCode code, float value) override;
TBall* AddBall(float x, float y); TBall* AddBall(float x, float y);
int BallCountInRect(const RectF& rect); int BallCountInRect(const RectF& rect);
int BallCountInRect(const vector2& pos, float margin);
static void EndGame_timeout(int timerId, void* caller); static void EndGame_timeout(int timerId, void* caller);
static void LightShow_timeout(int timerId, void* caller); static void LightShow_timeout(int timerId, void* caller);

View File

@ -33,7 +33,7 @@ DatFile* pb::record_table = nullptr;
int pb::time_ticks = 0; int pb::time_ticks = 0;
GameModes pb::game_mode = GameModes::GameOver; GameModes pb::game_mode = GameModes::GameOver;
float pb::time_now = 0, pb::time_next = 0, pb::time_ticks_remainder = 0; float pb::time_now = 0, pb::time_next = 0, pb::time_ticks_remainder = 0;
float pb::BallMaxSpeed, pb::BallHalfRadius, pb::ball_collision_dist; float pb::BallMaxSpeed, pb::BallHalfRadius, pb::BallToBallCollisionDistance;
bool pb::FullTiltMode = false, pb::FullTiltDemoMode = false, pb::cheat_mode = false, pb::demo_mode = false; bool pb::FullTiltMode = false, pb::FullTiltDemoMode = false, pb::cheat_mode = false, pb::demo_mode = false;
std::string pb::DatFileName, pb::BasePath; std::string pb::DatFileName, pb::BasePath;
ImU32 pb::TextBoxColor; ImU32 pb::TextBoxColor;
@ -106,7 +106,7 @@ int pb::init()
auto ball = MainTable->BallList.at(0); auto ball = MainTable->BallList.at(0);
BallMaxSpeed = ball->Radius * 200.0f; BallMaxSpeed = ball->Radius * 200.0f;
BallHalfRadius = ball->Radius * 0.5f; BallHalfRadius = ball->Radius * 0.5f;
ball_collision_dist = (ball->Radius + BallHalfRadius) * 2.0f; BallToBallCollisionDistance = (ball->Radius + BallHalfRadius) * 2.0f;
int red = 255, green = 255, blue = 255; int red = 255, green = 255, blue = 255;
auto fontColor = get_rc_string(Msg::TextBoxColor); auto fontColor = get_rc_string(Msg::TextBoxColor);
@ -424,8 +424,7 @@ void pb::timed_frame(float timeNow, float timeDelta, bool drawBalls)
auto distance = TTableLayer::edge_manager->FindCollisionDistance(&ray, ball, &edge); auto distance = TTableLayer::edge_manager->FindCollisionDistance(&ray, ball, &edge);
if (distance > 0.0f) if (distance > 0.0f)
{ {
// Todo: ball to ball collision distance = BallToBallCollision(ray, *ball, &edge, distance);
//distance = ball_to_ball_collision();
} }
if (ball->EdgeCollisionResetFlag) if (ball->EdgeCollisionResetFlag)
{ {
@ -605,9 +604,13 @@ void pb::InputDown(GameInput input)
switch (input.Value) switch (input.Value)
{ {
case 'b': case 'b':
if (MainTable->AddBall(6.0f, 7.0f)) {
MainTable->MultiballCount++; vector2 pos{6.0f, 7.0f};
break; if (!MainTable->BallCountInRect(pos, MainTable->CollisionCompOffset * 1.2f) && MainTable->AddBall(
pos.X, pos.Y))
MainTable->MultiballCount++;
break;
}
case 'h': case 'h':
{ {
high_score_struct entry{{0}, 1000000000}; high_score_struct entry{{0}, 1000000000};
@ -754,3 +757,27 @@ void pb::ShowMessageBox(Uint32 flags, LPCSTR title, LPCSTR message)
fprintf(flags == SDL_MESSAGEBOX_ERROR ? stderr : stdout, "BL error: %s\n%s\n", title, message); fprintf(flags == SDL_MESSAGEBOX_ERROR ? stderr : stdout, "BL error: %s\n%s\n", title, message);
SDL_ShowSimpleMessageBox(flags, title, message, winmain::MainWindow); SDL_ShowSimpleMessageBox(flags, title, message, winmain::MainWindow);
} }
float pb::BallToBallCollision(const ray_type& ray, const TBall& ball, TEdgeSegment** edge, float collisionDistance)
{
for (auto curBall : MainTable->BallList)
{
if (curBall->ActiveFlagPtr && curBall != &ball && (curBall->CollisionMask & ball.CollisionMask) != 0 &&
std::abs(curBall->Position.X - ball.Position.X) < BallToBallCollisionDistance &&
std::abs(curBall->Position.Y - ball.Position.Y) < BallToBallCollisionDistance)
{
auto distance = curBall->FindCollisionDistance(ray);
if (distance < 1e9f)
{
distance = std::max(0.0f, distance - 0.002f);
if (collisionDistance > distance)
{
collisionDistance = distance;
*edge = curBall;
}
}
}
}
return collisionDistance;
}

View File

@ -1,6 +1,8 @@
#pragma once #pragma once
#include "high_score.h" #include "high_score.h"
class TEdgeSegment;
struct ray_type;
struct GameInput; struct GameInput;
class TPinballTable; class TPinballTable;
class DatFile; class DatFile;
@ -43,7 +45,7 @@ class pb
public: public:
static int time_ticks; static int time_ticks;
static float time_now, time_next, time_ticks_remainder; static float time_now, time_next, time_ticks_remainder;
static float BallMaxSpeed, BallHalfRadius, ball_collision_dist; static float BallMaxSpeed, BallHalfRadius, BallToBallCollisionDistance;
static GameModes game_mode; static GameModes game_mode;
static bool cheat_mode; static bool cheat_mode;
static DatFile* record_table; static DatFile* record_table;
@ -81,4 +83,5 @@ public:
static void ShowMessageBox(Uint32 flags, LPCSTR title, LPCSTR message); static void ShowMessageBox(Uint32 flags, LPCSTR title, LPCSTR message);
private: private:
static bool demo_mode; static bool demo_mode;
static float BallToBallCollision(const ray_type& ray, const TBall& ball, TEdgeSegment** edge, float collisionDistance);
}; };