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 "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{};
char ballGroupName[10]{"ball"};
@ -22,19 +23,29 @@ TBall::TBall(TPinballTable* table) : TPinballComponent(table, -1, false)
CollisionComp = nullptr;
EdgeCollisionCount = 0;
TimeDelta = 0.0;
CollisionMask = 1;
CollisionFlag = 0;
Speed = 0.0;
Direction.Y = 0.0;
Direction.X = 0.0;
Position.X = 0.0;
Position.Y = 0.0;
HasGroupFlag = false;
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*/
auto groupIndex = loader::query_handle(ballGroupName);
groupIndex = loader::query_handle(ballGroupName);
if (groupIndex < 0)
{
ballGroupName[4] = '0' + fullscrn::GetResolution();
@ -139,6 +150,47 @@ void TBall::throw_ball(vector3* direction, float angleMult, float speedMult1, fl
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()
{
return TTableLayer::edge_manager->NormalizeBox(Position);

View File

@ -1,14 +1,12 @@
#pragma once
#include "maths.h"
#include "TPinballComponent.h"
#include "TCollisionComponent.h"
#include "TEdgeSegment.h"
class TCollisionComponent;
class TEdgeSegment;
class TBall : public TPinballComponent
class TBall : public TCollisionComponent, public TEdgeSegment
{
public :
TBall(TPinballTable* table);
TBall(TPinballTable* table, int groupIndex);
void Repaint();
void not_again(TEdgeSegment* edge);
bool already_hit(TEdgeSegment* edge);
@ -16,6 +14,9 @@ public :
vector2 get_coordinates() override;
void Disable();
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 PrevPosition{};

View File

@ -12,9 +12,9 @@ TCircle::TCircle(TCollisionComponent* collComp, char* activeFlag, unsigned colli
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)

View File

@ -10,7 +10,7 @@ public:
TCircle(TCollisionComponent* collComp, char* activeFlag, unsigned int collisionGroup, vector2* center,
float radius);
float FindCollisionDistance(ray_type* ray) override;
float FindCollisionDistance(const ray_type& ray) override;
void EdgeCollision(TBall* ball, float distance) 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)
{
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))
{
@ -84,7 +84,7 @@ int TEdgeManager::TestGridBox(int x, int y, float* distPtr, TEdgeSegment** edgeD
*edgePtr = edge;
++edgePtr;
edge->ProcessedFlag = 1;
auto dist = edge->FindCollisionDistance(ray);
auto dist = edge->FindCollisionDistance(*ray);
if (dist < *distPtr)
{
*distPtr = dist;

View File

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

View File

@ -15,7 +15,7 @@ class TEdgeSegment
{
public:
TCollisionComponent* CollisionComponent;
char* ActiveFlag;
char* ActiveFlagPtr;
char ProcessedFlag;
void* WallValue{};
unsigned int CollisionGroup;
@ -26,7 +26,7 @@ public:
virtual void EdgeCollision(TBall* ball, float distance) = 0;
virtual void port_draw();
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,
unsigned int collisionGroup, float offset, size_t wallValue);

View File

@ -104,12 +104,12 @@ void TFlipperEdge::port_draw()
set_control_points(CurrentAngle);
}
float TFlipperEdge::FindCollisionDistance(ray_type* ray)
float TFlipperEdge::FindCollisionDistance(const ray_type& ray)
{
ray_type dstRay{};
if (ControlPointDirtyFlag)
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)
return 1e9f;

View File

@ -12,7 +12,7 @@ public:
vector3* origin, vector3* vecT1, vector3* vecT2, float extendSpeed, float retractSpeed, float collMult,
float elasticity, float smoothness);
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 place_in_grid(RectF* aabb) override;
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);
}
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)

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, const vector2& start, const vector2& end);
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 place_in_grid(RectF* aabb) override;
};

View File

@ -625,7 +625,7 @@ TBall* TPinballTable::AddBall(float x, float y)
{
if (BallList.size() >= 20)
return nullptr;
ball = new TBall(this);
ball = new TBall(this, -1);
BallList.push_back(ball);
}
@ -655,6 +655,16 @@ int TPinballTable::BallCountInRect(const RectF& rect)
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)
{
auto table = static_cast<TPinballTable*>(caller);

View File

@ -37,6 +37,7 @@ public:
int Message(MessageCode code, float value) override;
TBall* AddBall(float x, float y);
int BallCountInRect(const RectF& rect);
int BallCountInRect(const vector2& pos, float margin);
static void EndGame_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;
GameModes pb::game_mode = GameModes::GameOver;
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;
std::string pb::DatFileName, pb::BasePath;
ImU32 pb::TextBoxColor;
@ -106,7 +106,7 @@ int pb::init()
auto ball = MainTable->BallList.at(0);
BallMaxSpeed = ball->Radius * 200.0f;
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;
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);
if (distance > 0.0f)
{
// Todo: ball to ball collision
//distance = ball_to_ball_collision();
distance = BallToBallCollision(ray, *ball, &edge, distance);
}
if (ball->EdgeCollisionResetFlag)
{
@ -605,9 +604,13 @@ void pb::InputDown(GameInput input)
switch (input.Value)
{
case 'b':
if (MainTable->AddBall(6.0f, 7.0f))
{
vector2 pos{6.0f, 7.0f};
if (!MainTable->BallCountInRect(pos, MainTable->CollisionCompOffset * 1.2f) && MainTable->AddBall(
pos.X, pos.Y))
MainTable->MultiballCount++;
break;
}
case 'h':
{
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);
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
#include "high_score.h"
class TEdgeSegment;
struct ray_type;
struct GameInput;
class TPinballTable;
class DatFile;
@ -43,7 +45,7 @@ class pb
public:
static int time_ticks;
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 bool cheat_mode;
static DatFile* record_table;
@ -81,4 +83,5 @@ public:
static void ShowMessageBox(Uint32 flags, LPCSTR title, LPCSTR message);
private:
static bool demo_mode;
static float BallToBallCollision(const ray_type& ray, const TBall& ball, TEdgeSegment** edge, float collisionDistance);
};