mirror of
https://github.com/Floriansylvain/SFMLplayground.git
synced 2026-08-19 11:43:24 +02:00
refac: project overhaul, cpp industry standards and google code style
This commit is contained in:
+280
@@ -0,0 +1,280 @@
|
||||
---
|
||||
Language: Cpp
|
||||
# BasedOnStyle: Google
|
||||
AccessModifierOffset: -1
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignArrayOfStructures: None
|
||||
AlignConsecutiveAssignments:
|
||||
Enabled: false
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCompound: false
|
||||
AlignFunctionPointers: false
|
||||
PadOperators: true
|
||||
AlignConsecutiveBitFields:
|
||||
Enabled: false
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCompound: false
|
||||
AlignFunctionPointers: false
|
||||
PadOperators: false
|
||||
AlignConsecutiveDeclarations:
|
||||
Enabled: false
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCompound: false
|
||||
AlignFunctionPointers: false
|
||||
PadOperators: false
|
||||
AlignConsecutiveMacros:
|
||||
Enabled: false
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCompound: false
|
||||
AlignFunctionPointers: false
|
||||
PadOperators: false
|
||||
AlignConsecutiveShortCaseStatements:
|
||||
Enabled: false
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCaseColons: false
|
||||
AlignEscapedNewlines: Left
|
||||
AlignOperands: Align
|
||||
AlignTrailingComments:
|
||||
Kind: Always
|
||||
OverEmptyLines: 0
|
||||
AllowAllArgumentsOnNextLine: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowBreakBeforeNoexceptSpecifier: Never
|
||||
AllowShortBlocksOnASingleLine: Never
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortCompoundRequirementOnASingleLine: true
|
||||
AllowShortEnumsOnASingleLine: true
|
||||
AllowShortFunctionsOnASingleLine: All
|
||||
AllowShortIfStatementsOnASingleLine: WithoutElse
|
||||
AllowShortLambdasOnASingleLine: All
|
||||
AllowShortLoopsOnASingleLine: true
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: true
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
AttributeMacros:
|
||||
- __capability
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
BitFieldColonSpacing: Both
|
||||
BraceWrapping:
|
||||
AfterCaseLabel: false
|
||||
AfterClass: false
|
||||
AfterControlStatement: Never
|
||||
AfterEnum: false
|
||||
AfterExternBlock: false
|
||||
AfterFunction: false
|
||||
AfterNamespace: false
|
||||
AfterObjCDeclaration: false
|
||||
AfterStruct: false
|
||||
AfterUnion: false
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
BeforeLambdaBody: false
|
||||
BeforeWhile: false
|
||||
IndentBraces: false
|
||||
SplitEmptyFunction: true
|
||||
SplitEmptyRecord: true
|
||||
SplitEmptyNamespace: true
|
||||
BreakAdjacentStringLiterals: true
|
||||
BreakAfterAttributes: Leave
|
||||
BreakAfterJavaFieldAnnotations: false
|
||||
BreakArrays: true
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeConceptDeclarations: Always
|
||||
BreakBeforeBraces: Attach
|
||||
BreakBeforeInlineASMColon: OnlyMultiline
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializers: BeforeColon
|
||||
BreakInheritanceList: BeforeColon
|
||||
BreakStringLiterals: true
|
||||
ColumnLimit: 80
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
CompactNamespaces: false
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
Cpp11BracedListStyle: true
|
||||
DerivePointerAlignment: true
|
||||
DisableFormat: false
|
||||
EmptyLineAfterAccessModifier: Never
|
||||
EmptyLineBeforeAccessModifier: LogicalBlock
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
FixNamespaceComments: true
|
||||
ForEachMacros:
|
||||
- foreach
|
||||
- Q_FOREACH
|
||||
- BOOST_FOREACH
|
||||
IfMacros:
|
||||
- KJ_IF_MAYBE
|
||||
IncludeBlocks: Regroup
|
||||
IncludeCategories:
|
||||
- Regex: '^<ext/.*\.h>'
|
||||
Priority: 2
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
- Regex: '^<.*\.h>'
|
||||
Priority: 1
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
- Regex: '^<.*'
|
||||
Priority: 2
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
- Regex: '.*'
|
||||
Priority: 3
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
IncludeIsMainRegex: '([-_](test|unittest))?$'
|
||||
IncludeIsMainSourceRegex: ''
|
||||
IndentAccessModifiers: false
|
||||
IndentCaseBlocks: false
|
||||
IndentCaseLabels: true
|
||||
IndentExternBlock: AfterExternBlock
|
||||
IndentGotoLabels: true
|
||||
IndentPPDirectives: None
|
||||
IndentRequiresClause: true
|
||||
IndentWidth: 2
|
||||
IndentWrappedFunctionNames: false
|
||||
InsertBraces: false
|
||||
InsertNewlineAtEOF: false
|
||||
InsertTrailingCommas: None
|
||||
IntegerLiteralSeparator:
|
||||
Binary: 0
|
||||
BinaryMinDigits: 0
|
||||
Decimal: 0
|
||||
DecimalMinDigits: 0
|
||||
Hex: 0
|
||||
HexMinDigits: 0
|
||||
JavaScriptQuotes: Leave
|
||||
JavaScriptWrapImports: true
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
KeepEmptyLinesAtEOF: false
|
||||
LambdaBodyIndentation: Signature
|
||||
LineEnding: DeriveLF
|
||||
MacroBlockBegin: ''
|
||||
MacroBlockEnd: ''
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: None
|
||||
ObjCBinPackProtocolList: Never
|
||||
ObjCBlockIndentWidth: 2
|
||||
ObjCBreakBeforeNestedBlockParam: true
|
||||
ObjCSpaceAfterProperty: false
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
PackConstructorInitializers: NextLine
|
||||
PenaltyBreakAssignment: 2
|
||||
PenaltyBreakBeforeFirstCallParameter: 1
|
||||
PenaltyBreakComment: 300
|
||||
PenaltyBreakFirstLessLess: 120
|
||||
PenaltyBreakOpenParenthesis: 0
|
||||
PenaltyBreakScopeResolution: 500
|
||||
PenaltyBreakString: 1000
|
||||
PenaltyBreakTemplateDeclaration: 10
|
||||
PenaltyExcessCharacter: 1000000
|
||||
PenaltyIndentedWhitespace: 0
|
||||
PenaltyReturnTypeOnItsOwnLine: 200
|
||||
PointerAlignment: Left
|
||||
PPIndentWidth: -1
|
||||
QualifierAlignment: Leave
|
||||
RawStringFormats:
|
||||
- Language: Cpp
|
||||
Delimiters:
|
||||
- cc
|
||||
- CC
|
||||
- cpp
|
||||
- Cpp
|
||||
- CPP
|
||||
- 'c++'
|
||||
- 'C++'
|
||||
CanonicalDelimiter: ''
|
||||
BasedOnStyle: google
|
||||
- Language: TextProto
|
||||
Delimiters:
|
||||
- pb
|
||||
- PB
|
||||
- proto
|
||||
- PROTO
|
||||
EnclosingFunctions:
|
||||
- EqualsProto
|
||||
- EquivToProto
|
||||
- PARSE_PARTIAL_TEXT_PROTO
|
||||
- PARSE_TEST_PROTO
|
||||
- PARSE_TEXT_PROTO
|
||||
- ParseTextOrDie
|
||||
- ParseTextProtoOrDie
|
||||
- ParseTestProto
|
||||
- ParsePartialTestProto
|
||||
CanonicalDelimiter: pb
|
||||
BasedOnStyle: google
|
||||
ReferenceAlignment: Pointer
|
||||
ReflowComments: true
|
||||
RemoveBracesLLVM: false
|
||||
RemoveParentheses: Leave
|
||||
RemoveSemicolon: false
|
||||
RequiresClausePosition: OwnLine
|
||||
RequiresExpressionIndentation: OuterScope
|
||||
SeparateDefinitionBlocks: Leave
|
||||
ShortNamespaceLines: 1
|
||||
SkipMacroDefinitionBody: false
|
||||
SortIncludes: CaseSensitive
|
||||
SortJavaStaticImport: Before
|
||||
SortUsingDeclarations: LexicographicNumeric
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterLogicalNot: false
|
||||
SpaceAfterTemplateKeyword: true
|
||||
SpaceAroundPointerQualifiers: Default
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCaseColon: false
|
||||
SpaceBeforeCpp11BracedList: false
|
||||
SpaceBeforeCtorInitializerColon: true
|
||||
SpaceBeforeInheritanceColon: true
|
||||
SpaceBeforeJsonColon: false
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceBeforeParensOptions:
|
||||
AfterControlStatements: true
|
||||
AfterForeachMacros: true
|
||||
AfterFunctionDefinitionName: false
|
||||
AfterFunctionDeclarationName: false
|
||||
AfterIfMacros: true
|
||||
AfterOverloadedOperator: false
|
||||
AfterPlacementOperator: true
|
||||
AfterRequiresInClause: false
|
||||
AfterRequiresInExpression: false
|
||||
BeforeNonEmptyParentheses: false
|
||||
SpaceBeforeRangeBasedForLoopColon: true
|
||||
SpaceBeforeSquareBrackets: false
|
||||
SpaceInEmptyBlock: false
|
||||
SpacesBeforeTrailingComments: 2
|
||||
SpacesInAngles: Never
|
||||
SpacesInContainerLiterals: true
|
||||
SpacesInLineCommentPrefix:
|
||||
Minimum: 1
|
||||
Maximum: -1
|
||||
SpacesInParens: Never
|
||||
SpacesInParensOptions:
|
||||
InCStyleCasts: false
|
||||
InConditionalStatements: false
|
||||
InEmptyParentheses: false
|
||||
Other: false
|
||||
SpacesInSquareBrackets: false
|
||||
Standard: Auto
|
||||
StatementAttributeLikeMacros:
|
||||
- Q_EMIT
|
||||
StatementMacros:
|
||||
- Q_UNUSED
|
||||
- QT_REQUIRE_VERSION
|
||||
TabWidth: 8
|
||||
UseTab: Never
|
||||
VerilogBreakBetweenInstancePorts: true
|
||||
WhitespaceSensitiveMacros:
|
||||
- BOOST_PP_STRINGIZE
|
||||
- CF_SWIFT_NAME
|
||||
- NS_SWIFT_NAME
|
||||
- PP_STRINGIZE
|
||||
- STRINGIZE
|
||||
...
|
||||
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
build
|
||||
build/
|
||||
cmake-build-debug/
|
||||
out
|
||||
.cache
|
||||
.idea
|
||||
|
||||
+53
-51
@@ -4,12 +4,12 @@
|
||||
|
||||
#include "Constants.hpp"
|
||||
|
||||
Ball::Ball(float radius, const sf::Vector2f& pos, const sf::Vector2f& vel,
|
||||
const sf::Color& color)
|
||||
: m_radius(radius),
|
||||
m_velocity(vel),
|
||||
Ball::Ball(const float radius, const sf::Vector2f &pos, const sf::Vector2f &vel,
|
||||
const sf::Color &color)
|
||||
: m_velocity(vel),
|
||||
m_lastPosition(pos),
|
||||
m_pixelVelocity(vel),
|
||||
m_radius(radius),
|
||||
m_baseColor(color) {
|
||||
m_shape.setRadius(radius);
|
||||
m_shape.setOrigin(sf::Vector2f(radius, radius));
|
||||
@@ -17,12 +17,12 @@ Ball::Ball(float radius, const sf::Vector2f& pos, const sf::Vector2f& vel,
|
||||
m_shape.setFillColor(color);
|
||||
}
|
||||
|
||||
void Ball::update(float dt, const sf::Vector2f& windowSize) {
|
||||
void Ball::update(const float dt, const sf::Vector2f &windowSize) {
|
||||
if (m_atRest) return;
|
||||
m_velocity.y += Constants::GRAVITY * dt;
|
||||
m_shape.move(m_velocity * dt);
|
||||
|
||||
sf::Vector2f currentPosition = m_shape.getPosition();
|
||||
const sf::Vector2f currentPosition = m_shape.getPosition();
|
||||
m_pixelVelocity = (currentPosition - m_lastPosition) / dt;
|
||||
m_lastPosition = currentPosition;
|
||||
|
||||
@@ -31,59 +31,61 @@ void Ball::update(float dt, const sf::Vector2f& windowSize) {
|
||||
}
|
||||
|
||||
void Ball::updateColor() {
|
||||
float currentSpeed =
|
||||
const float currentSpeed =
|
||||
std::sqrt(m_velocity.x * m_velocity.x + m_velocity.y * m_velocity.y);
|
||||
|
||||
static float lastSpeed = 0.0f;
|
||||
if (std::abs(currentSpeed - lastSpeed) < 10.0f) {
|
||||
return;
|
||||
}
|
||||
if (std::abs(currentSpeed - lastSpeed) < 10.0f) return;
|
||||
lastSpeed = currentSpeed;
|
||||
|
||||
float speed =
|
||||
const float speed =
|
||||
std::sqrt(m_velocity.x * m_velocity.x + m_velocity.y * m_velocity.y);
|
||||
|
||||
const float MAX_SPEED = 2000.0f;
|
||||
float t = std::min(speed / MAX_SPEED, 1.0f);
|
||||
const float t = std::min(speed / 2000.0f, 1.0f);
|
||||
|
||||
sf::Color targetColor;
|
||||
|
||||
if (t < 0.33f) { // Red to orange
|
||||
float scaledT = t * 3.0f;
|
||||
if (t < 0.33f) {
|
||||
// Red to orange
|
||||
const float scaledT = t * 3.0f;
|
||||
targetColor.r = static_cast<std::uint8_t>(255);
|
||||
targetColor.g = static_cast<std::uint8_t>(0 + scaledT * 165);
|
||||
targetColor.b = static_cast<std::uint8_t>(0);
|
||||
} else if (t < 0.66f) { // Orange to yellow
|
||||
float scaledT = (t - 0.33f) * 3.0f;
|
||||
} else if (t < 0.66f) {
|
||||
// Orange to yellow
|
||||
const float scaledT = (t - 0.33f) * 3.0f;
|
||||
targetColor.r = static_cast<std::uint8_t>(255);
|
||||
targetColor.g = static_cast<std::uint8_t>(165 + scaledT * 90);
|
||||
targetColor.b = static_cast<std::uint8_t>(0);
|
||||
} else { // Yellow to white
|
||||
float scaledT = (t - 0.66f) * 3.0f;
|
||||
} else {
|
||||
// Yellow to white
|
||||
const float scaledT = (t - 0.66f) * 3.0f;
|
||||
targetColor.r = static_cast<std::uint8_t>(255);
|
||||
targetColor.g = static_cast<std::uint8_t>(255);
|
||||
targetColor.b = static_cast<std::uint8_t>(0 + scaledT * 255);
|
||||
}
|
||||
|
||||
sf::Color currentColor = m_shape.getFillColor();
|
||||
const sf::Color currentColor = m_shape.getFillColor();
|
||||
|
||||
const float TRANSITION_SPEED = 0.05f;
|
||||
constexpr float TRANSITION_SPEED = 0.05f;
|
||||
|
||||
sf::Color newColor;
|
||||
newColor.r = static_cast<std::uint8_t>(
|
||||
currentColor.r + TRANSITION_SPEED * (targetColor.r - currentColor.r));
|
||||
newColor.g = static_cast<std::uint8_t>(
|
||||
currentColor.g + TRANSITION_SPEED * (targetColor.g - currentColor.g));
|
||||
newColor.b = static_cast<std::uint8_t>(
|
||||
currentColor.b + TRANSITION_SPEED * (targetColor.b - currentColor.b));
|
||||
newColor.r = static_cast<std::uint8_t>(static_cast<float>(currentColor.r) +
|
||||
TRANSITION_SPEED *
|
||||
(targetColor.r - currentColor.r));
|
||||
newColor.g = static_cast<std::uint8_t>(static_cast<float>(currentColor.g) +
|
||||
TRANSITION_SPEED *
|
||||
(targetColor.g - currentColor.g));
|
||||
newColor.b = static_cast<std::uint8_t>(static_cast<float>(currentColor.b) +
|
||||
TRANSITION_SPEED *
|
||||
(targetColor.b - currentColor.b));
|
||||
newColor.a = 255;
|
||||
|
||||
m_shape.setFillColor(newColor);
|
||||
}
|
||||
|
||||
void Ball::draw(sf::RenderWindow& window) { window.draw(m_shape); }
|
||||
void Ball::draw(sf::RenderWindow &window) { window.draw(m_shape); }
|
||||
|
||||
void Ball::applyImpulse(const sf::Vector2f& impulse) {
|
||||
void Ball::applyImpulse(const sf::Vector2f &impulse) {
|
||||
m_velocity += impulse;
|
||||
m_atRest = false;
|
||||
}
|
||||
@@ -94,12 +96,13 @@ sf::Vector2f Ball::getVelocity() const { return m_velocity; }
|
||||
|
||||
bool Ball::isAtRest() const { return m_atRest; }
|
||||
|
||||
void Ball::handleWallCollision(const sf::Vector2f& windowSize) {
|
||||
void Ball::handleWallCollision(const sf::Vector2f &windowSize) {
|
||||
sf::Vector2f pos = m_shape.getPosition();
|
||||
|
||||
auto handleAxis = [&](int axis, float min, float max, float& velocity,
|
||||
float radius, float restitution) {
|
||||
float value = (axis == 0) ? pos.x : pos.y;
|
||||
auto handleAxis = [&](const int axis, const float min, const float max,
|
||||
float &velocity, const float radius,
|
||||
const float restitution) {
|
||||
float value = axis == 0 ? pos.x : pos.y;
|
||||
|
||||
if (value - radius < min) {
|
||||
value = min + radius;
|
||||
@@ -133,31 +136,30 @@ void Ball::handleWallCollision(const sf::Vector2f& windowSize) {
|
||||
m_shape.setPosition(pos);
|
||||
}
|
||||
|
||||
void Ball::resolveCollision(Ball& other) {
|
||||
std::lock_guard<std::mutex> lockA(m_mutex);
|
||||
std::lock_guard<std::mutex> lockB(other.m_mutex);
|
||||
void Ball::resolveCollision(Ball &other) {
|
||||
std::lock_guard lockA(m_mutex);
|
||||
std::lock_guard lockB(other.m_mutex);
|
||||
|
||||
sf::Vector2f posA = getPosition();
|
||||
sf::Vector2f posB = other.getPosition();
|
||||
sf::Vector2f delta = posB - posA;
|
||||
float dist = std::sqrt(delta.x * delta.x + delta.y * delta.y);
|
||||
float minDist = m_radius + other.m_radius;
|
||||
const sf::Vector2f posA = getPosition();
|
||||
const sf::Vector2f posB = other.getPosition();
|
||||
const sf::Vector2f delta = posB - posA;
|
||||
const float dist = std::sqrt(delta.x * delta.x + delta.y * delta.y);
|
||||
const float minDist = m_radius + other.m_radius;
|
||||
|
||||
if (dist >= minDist || dist < 1e-6f) return;
|
||||
|
||||
sf::Vector2f normal = delta / dist;
|
||||
float overlap = minDist - dist;
|
||||
const sf::Vector2f normal = delta / dist;
|
||||
const float overlap = minDist - dist;
|
||||
m_shape.move(-normal * (overlap / 2.f));
|
||||
other.m_shape.move(normal * (overlap / 2.f));
|
||||
|
||||
sf::Vector2f vA = m_velocity;
|
||||
sf::Vector2f vB = other.m_velocity;
|
||||
float vA_n = vA.x * normal.x + vA.y * normal.y;
|
||||
float vB_n = vB.x * normal.x + vB.y * normal.y;
|
||||
const sf::Vector2f vA = m_velocity;
|
||||
const sf::Vector2f vB = other.m_velocity;
|
||||
const float vA_n = vA.x * normal.x + vA.y * normal.y;
|
||||
const float vB_n = vB.x * normal.x + vB.y * normal.y;
|
||||
|
||||
float restitution = 0.95f;
|
||||
float vA_n_new = vB_n * restitution;
|
||||
float vB_n_new = vA_n * restitution;
|
||||
const float vA_n_new = vB_n * Constants::RESTITUTION;
|
||||
const float vB_n_new = vA_n * Constants::RESTITUTION;
|
||||
|
||||
m_velocity += (vA_n_new - vA_n) * normal;
|
||||
other.m_velocity += (vB_n_new - vB_n) * normal;
|
||||
|
||||
+15
-9
@@ -4,8 +4,7 @@
|
||||
|
||||
#include "PhysicalObject.hpp"
|
||||
|
||||
class Ball : public PhysicalObject {
|
||||
private:
|
||||
class Ball final : public PhysicalObject {
|
||||
// Position and physics data
|
||||
sf::Vector2f m_velocity;
|
||||
sf::Vector2f m_lastPosition;
|
||||
@@ -20,21 +19,28 @@ class Ball : public PhysicalObject {
|
||||
bool m_atRest = false;
|
||||
mutable std::mutex m_mutex;
|
||||
|
||||
void handleWallCollision(const sf::Vector2f& windowSize);
|
||||
void handleWallCollision(const sf::Vector2f &windowSize);
|
||||
|
||||
void updateColor();
|
||||
|
||||
public:
|
||||
Ball(float radius, const sf::Vector2f& pos, const sf::Vector2f& vel,
|
||||
const sf::Color& color);
|
||||
Ball(float radius, const sf::Vector2f &pos, const sf::Vector2f &vel,
|
||||
const sf::Color &color);
|
||||
|
||||
void update(float dt, const sf::Vector2f& windowSize) override;
|
||||
void draw(sf::RenderWindow& window) override;
|
||||
void applyImpulse(const sf::Vector2f& impulse) override;
|
||||
void resolveCollision(Ball& other);
|
||||
void update(float dt, const sf::Vector2f &windowSize) override;
|
||||
|
||||
void draw(sf::RenderWindow &window) override;
|
||||
|
||||
void applyImpulse(const sf::Vector2f &impulse) override;
|
||||
|
||||
void resolveCollision(Ball &other);
|
||||
|
||||
sf::Vector2f getPosition() const;
|
||||
|
||||
sf::Vector2f getVelocity() const;
|
||||
|
||||
bool isAtRest() const;
|
||||
|
||||
sf::Vector2f getPixelVelocity() const { return m_pixelVelocity; }
|
||||
float getRadius() const { return m_radius; }
|
||||
sf::Color getColor() const { return m_shape.getFillColor(); }
|
||||
|
||||
+19
-16
@@ -1,33 +1,36 @@
|
||||
#include "BallFactory.hpp"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
#include <random>
|
||||
|
||||
#include "Constants.hpp"
|
||||
|
||||
namespace BallFactory {
|
||||
std::unique_ptr<Ball> generateRandBall() {
|
||||
float radius = Constants::BALL_RADIUS;
|
||||
int diameter = static_cast<int>(2 * radius);
|
||||
int maxX = Constants::WIDTH - diameter;
|
||||
int maxY = Constants::HEIGHT - diameter;
|
||||
float x = static_cast<float>((std::rand() % maxX) + radius);
|
||||
float y = static_cast<float>((std::rand() % maxY) + radius);
|
||||
float vel = static_cast<float>((std::rand() % 401) - 200);
|
||||
static std::random_device rd;
|
||||
static std::mt19937 rng(rd());
|
||||
constexpr float x1 = Constants::WIDTH - Constants::BALL_RADIUS * 2;
|
||||
constexpr float y1 = Constants::HEIGHT - Constants::BALL_RADIUS * 2;
|
||||
std::uniform_real_distribution posXDist(Constants::BALL_RADIUS, x1);
|
||||
std::uniform_real_distribution posYDist(Constants::BALL_RADIUS, y1);
|
||||
std::uniform_real_distribution velDist(-200.0f, 200.0f);
|
||||
std::uniform_int_distribution colorDist(64, 255);
|
||||
|
||||
auto randColorComp = []() {
|
||||
return static_cast<std::uint8_t>(64 + (std::rand() % (256 - 64)));
|
||||
};
|
||||
sf::Color color(randColorComp(), randColorComp(), randColorComp());
|
||||
float radius = Constants::BALL_RADIUS;
|
||||
const float x = posXDist(rng);
|
||||
const float y = posYDist(rng);
|
||||
const float vel = velDist(rng);
|
||||
|
||||
sf::Color color(static_cast<std::uint8_t>(colorDist(rng)),
|
||||
static_cast<std::uint8_t>(colorDist(rng)),
|
||||
static_cast<std::uint8_t>(colorDist(rng)));
|
||||
|
||||
auto position = sf::Vector2f(x, y);
|
||||
auto velocity = sf::Vector2f(vel, vel);
|
||||
return std::make_unique<Ball>(radius, position, velocity, color);
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<Ball>> generateBalls() {
|
||||
std::vector<std::unique_ptr<Ball>> balls;
|
||||
std::srand(static_cast<unsigned>(std::time(nullptr)));
|
||||
std::vector<std::unique_ptr<Ball> > generateBalls() {
|
||||
std::vector<std::unique_ptr<Ball> > balls;
|
||||
|
||||
for (int i = 0; i < Constants::BALL_QUANTITY; ++i) {
|
||||
auto ball = generateRandBall();
|
||||
|
||||
+2
-1
@@ -6,5 +6,6 @@
|
||||
|
||||
namespace BallFactory {
|
||||
std::unique_ptr<Ball> generateRandBall();
|
||||
std::vector<std::unique_ptr<Ball>> generateBalls();
|
||||
|
||||
std::vector<std::unique_ptr<Ball> > generateBalls();
|
||||
} // namespace BallFactory
|
||||
+23
-23
@@ -5,24 +5,24 @@
|
||||
#include <cmath>
|
||||
|
||||
BatchRenderer::BatchRenderer() : m_vertices(sf::PrimitiveType::Triangles) {
|
||||
const unsigned int size = 64;
|
||||
constexpr unsigned int size = 64;
|
||||
sf::Image image(sf::Vector2u(size, size), sf::Color::Transparent);
|
||||
|
||||
unsigned int radius = size / 2;
|
||||
sf::Vector2u center(radius, radius);
|
||||
constexpr unsigned int radius = size / 2;
|
||||
constexpr sf::Vector2u center(radius, radius);
|
||||
|
||||
for (unsigned int y = 0; y < size; ++y) {
|
||||
for (unsigned int x = 0; x < size; ++x) {
|
||||
sf::Vector2u pixel(x, y);
|
||||
int dx = static_cast<int>(center.x) - static_cast<int>(pixel.x);
|
||||
int dy = static_cast<int>(center.y) - static_cast<int>(pixel.y);
|
||||
float distance = std::sqrt(dx * dx + dy * dy);
|
||||
const sf::Vector2u pixel(x, y);
|
||||
const int dx = static_cast<int>(center.x) - static_cast<int>(pixel.x);
|
||||
const int dy = static_cast<int>(center.y) - static_cast<int>(pixel.y);
|
||||
|
||||
if (distance <= radius) {
|
||||
if (const auto distance =
|
||||
static_cast<float>(std::sqrt(dx * dx + dy * dy));
|
||||
distance <= radius) {
|
||||
float alpha = 255.0f;
|
||||
if (distance > radius - 2.0f) {
|
||||
alpha =
|
||||
255.0f * (1.0f - (distance - (radius - 2.0f)) / 2.0f);
|
||||
alpha = 255.0f * (1.0f - (distance - (radius - 2.0f)) / 2.0f);
|
||||
}
|
||||
image.setPixel(
|
||||
sf::Vector2u(x, y),
|
||||
@@ -38,10 +38,10 @@ BatchRenderer::BatchRenderer() : m_vertices(sf::PrimitiveType::Triangles) {
|
||||
|
||||
void BatchRenderer::clear() { m_vertices.clear(); }
|
||||
|
||||
void BatchRenderer::addBall(const Ball& ball) {
|
||||
sf::Vector2f position = ball.getPosition();
|
||||
float radius = ball.getRadius();
|
||||
sf::Color color = ball.getColor();
|
||||
void BatchRenderer::addBall(const Ball &ball) {
|
||||
const sf::Vector2f position = ball.getPosition();
|
||||
const float radius = ball.getRadius();
|
||||
const sf::Color color = ball.getColor();
|
||||
|
||||
sf::Vertex topLeft;
|
||||
sf::Vertex topRight;
|
||||
@@ -50,16 +50,16 @@ void BatchRenderer::addBall(const Ball& ball) {
|
||||
|
||||
topLeft.position = sf::Vector2f(position.x - radius, position.y - radius);
|
||||
topRight.position = sf::Vector2f(position.x + radius, position.y - radius);
|
||||
bottomRight.position =
|
||||
sf::Vector2f(position.x + radius, position.y + radius);
|
||||
bottomLeft.position =
|
||||
sf::Vector2f(position.x - radius, position.y + radius);
|
||||
bottomRight.position = sf::Vector2f(position.x + radius, position.y + radius);
|
||||
bottomLeft.position = sf::Vector2f(position.x - radius, position.y + radius);
|
||||
|
||||
const float xSize = static_cast<float>(m_circleTexture.getSize().x);
|
||||
const float ySize = static_cast<float>(m_circleTexture.getSize().y);
|
||||
|
||||
topLeft.texCoords = sf::Vector2f(0, 0);
|
||||
topRight.texCoords = sf::Vector2f(m_circleTexture.getSize().x, 0);
|
||||
bottomRight.texCoords =
|
||||
sf::Vector2f(m_circleTexture.getSize().x, m_circleTexture.getSize().y);
|
||||
bottomLeft.texCoords = sf::Vector2f(0, m_circleTexture.getSize().y);
|
||||
topRight.texCoords = sf::Vector2f(xSize, 0);
|
||||
bottomRight.texCoords = sf::Vector2f(xSize, ySize);
|
||||
bottomLeft.texCoords = sf::Vector2f(0, ySize);
|
||||
|
||||
topLeft.color = color;
|
||||
topRight.color = color;
|
||||
@@ -75,7 +75,7 @@ void BatchRenderer::addBall(const Ball& ball) {
|
||||
m_vertices.append(bottomLeft);
|
||||
}
|
||||
|
||||
void BatchRenderer::draw(sf::RenderWindow& window) {
|
||||
void BatchRenderer::draw(sf::RenderWindow &window) const {
|
||||
if (m_vertices.getVertexCount() == 0) return;
|
||||
|
||||
sf::RenderStates states;
|
||||
|
||||
@@ -2,18 +2,19 @@
|
||||
#include <SFML/Graphics/RenderWindow.hpp>
|
||||
#include <SFML/Graphics/Texture.hpp>
|
||||
#include <SFML/Graphics/VertexArray.hpp>
|
||||
#include <vector>
|
||||
|
||||
#include "Ball.hpp"
|
||||
|
||||
class BatchRenderer {
|
||||
private:
|
||||
sf::VertexArray m_vertices;
|
||||
sf::Texture m_circleTexture;
|
||||
|
||||
public:
|
||||
BatchRenderer();
|
||||
|
||||
void clear();
|
||||
void addBall(const Ball& ball);
|
||||
void draw(sf::RenderWindow& window);
|
||||
|
||||
void addBall(const Ball &ball);
|
||||
|
||||
void draw(sf::RenderWindow &window) const;
|
||||
};
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
namespace Constants {
|
||||
constexpr unsigned WIDTH = 1920;
|
||||
constexpr unsigned HEIGHT = 1080;
|
||||
constexpr int WIDTH = 1920;
|
||||
constexpr int HEIGHT = 1080;
|
||||
constexpr float GRAVITY = 781.f;
|
||||
constexpr float RESTITUTION = 0.8f;
|
||||
constexpr float FRICTION = 0.9f;
|
||||
|
||||
+19
-18
@@ -6,41 +6,42 @@
|
||||
|
||||
#include "VectorMath.hpp"
|
||||
|
||||
float clamp(float value, float minVal, float maxVal) {
|
||||
float clamp(const float value, const float minVal, const float maxVal) {
|
||||
return std::max(minVal, std::min(value, maxVal));
|
||||
}
|
||||
|
||||
void DebugDraw::addLine(sf::VertexArray& lines, const sf::Vector2f& start,
|
||||
const sf::Vector2f& direction, float length,
|
||||
const sf::Color& color) {
|
||||
sf::Vector2f endPoint = start + VectorMath::normalize(direction) * length;
|
||||
void DebugDraw::addLine(sf::VertexArray &lines, const sf::Vector2f &start,
|
||||
const sf::Vector2f &direction, const float length,
|
||||
const sf::Color &color) {
|
||||
const sf::Vector2f endPoint =
|
||||
start + VectorMath::normalize(direction) * length;
|
||||
lines.append(sf::Vertex({start, color}));
|
||||
lines.append(sf::Vertex({endPoint, color}));
|
||||
}
|
||||
|
||||
void DebugDraw::addDirectionLine(sf::VertexArray& lines, const Ball* ball,
|
||||
const sf::RenderWindow& window) {
|
||||
sf::Vector2f ballCenter = ball->getPosition();
|
||||
sf::Vector2i mousePixel = sf::Mouse::getPosition(window);
|
||||
sf::Vector2f mouseWorld(static_cast<float>(mousePixel.x),
|
||||
void DebugDraw::addDirectionLine(sf::VertexArray &lines, const Ball *ball,
|
||||
const sf::RenderWindow &window) {
|
||||
const sf::Vector2f ballCenter = ball->getPosition();
|
||||
const sf::Vector2i mousePixel = sf::Mouse::getPosition(window);
|
||||
const sf::Vector2f mouseWorld(static_cast<float>(mousePixel.x),
|
||||
static_cast<float>(mousePixel.y));
|
||||
sf::Vector2f dir = mouseWorld - ballCenter;
|
||||
const sf::Vector2f dir = mouseWorld - ballCenter;
|
||||
|
||||
addLine(lines, ballCenter, dir, 100.f, sf::Color::Green);
|
||||
}
|
||||
|
||||
void DebugDraw::addVelocityLine(sf::VertexArray& lines, const Ball* ball) {
|
||||
void DebugDraw::addVelocityLine(sf::VertexArray &lines, const Ball *ball) {
|
||||
if (ball->isAtRest()) return;
|
||||
|
||||
sf::Vector2f ballCenter = ball->getPosition();
|
||||
sf::Vector2f velocity = ball->getVelocity();
|
||||
float velLength = VectorMath::length(velocity);
|
||||
float clampedLength = std::max(0.f, std::min(velLength, 100.f));
|
||||
const sf::Vector2f ballCenter = ball->getPosition();
|
||||
const sf::Vector2f velocity = ball->getVelocity();
|
||||
const float velLength = VectorMath::length(velocity);
|
||||
const float clampedLength = std::max(0.f, std::min(velLength, 100.f));
|
||||
|
||||
addLine(lines, ballCenter, velocity, clampedLength, sf::Color::Red);
|
||||
}
|
||||
|
||||
void DebugDraw::drawBatchedLines(sf::RenderWindow& window,
|
||||
const sf::VertexArray& lines) {
|
||||
void DebugDraw::drawBatchedLines(sf::RenderWindow &window,
|
||||
const sf::VertexArray &lines) {
|
||||
if (lines.getVertexCount() > 0) window.draw(lines);
|
||||
}
|
||||
|
||||
+11
-8
@@ -7,12 +7,15 @@
|
||||
|
||||
class DebugDraw {
|
||||
public:
|
||||
static void addLine(sf::VertexArray& lines, const sf::Vector2f& start,
|
||||
const sf::Vector2f& direction, float length,
|
||||
const sf::Color& color);
|
||||
static void addDirectionLine(sf::VertexArray& lines, const Ball* ball,
|
||||
const sf::RenderWindow& window);
|
||||
static void addVelocityLine(sf::VertexArray& lines, const Ball* ball);
|
||||
static void drawBatchedLines(sf::RenderWindow& window,
|
||||
const sf::VertexArray& lines);
|
||||
static void addLine(sf::VertexArray &lines, const sf::Vector2f &start,
|
||||
const sf::Vector2f &direction, float length,
|
||||
const sf::Color &color);
|
||||
|
||||
static void addDirectionLine(sf::VertexArray &lines, const Ball *ball,
|
||||
const sf::RenderWindow &window);
|
||||
|
||||
static void addVelocityLine(sf::VertexArray &lines, const Ball *ball);
|
||||
|
||||
static void drawBatchedLines(sf::RenderWindow &window,
|
||||
const sf::VertexArray &lines);
|
||||
};
|
||||
|
||||
+10
-9
@@ -2,25 +2,26 @@
|
||||
|
||||
#include <sstream>
|
||||
|
||||
DebugOverlay::DebugOverlay(const std::string& fontPath)
|
||||
DebugOverlay::DebugOverlay(const std::string &fontPath)
|
||||
: m_font(fontPath), m_text(m_font) {
|
||||
m_text.setCharacterSize(18);
|
||||
m_text.setFillColor(sf::Color::White);
|
||||
m_text.setPosition(sf::Vector2f(5.f, 5.f));
|
||||
}
|
||||
|
||||
void DebugOverlay::update(int drawCalls, float timeScale,
|
||||
sf::RenderWindow& window, size_t threadCount,
|
||||
size_t ballsPerThread) {
|
||||
float elapsed = m_fpsClock.restart().asSeconds();
|
||||
if (elapsed > 0.f) m_fps = static_cast<int>(1.f / elapsed);
|
||||
void DebugOverlay::update(const int drawCalls, const float timeScale,
|
||||
const sf::RenderWindow &window,
|
||||
const size_t threadCount,
|
||||
const size_t ballsPerThread) {
|
||||
const float elapsed = m_fpsClock.restart().asSeconds();
|
||||
if (elapsed > 0.2f) m_fps = 1.f / elapsed;
|
||||
|
||||
sf::Vector2i mousePos = sf::Mouse::getPosition(window);
|
||||
const sf::Vector2i mousePos = sf::Mouse::getPosition(window);
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << "Draw calls: " << drawCalls << "\n";
|
||||
oss << "Framerate: " << m_fps << " FPS\n";
|
||||
oss << "Frametime: " << (elapsed * 1000.f) << " ms\n";
|
||||
oss << "Frame time: " << (elapsed * 1000.f) << " ms\n";
|
||||
oss << "Mouse: " << mousePos.x << ", " << mousePos.y << "\n";
|
||||
oss << "Time scale: " << timeScale << "\n";
|
||||
oss << "\nThreads: " << threadCount;
|
||||
@@ -28,4 +29,4 @@ void DebugOverlay::update(int drawCalls, float timeScale,
|
||||
m_text.setString(oss.str());
|
||||
}
|
||||
|
||||
void DebugOverlay::draw(sf::RenderWindow& window) { window.draw(m_text); }
|
||||
void DebugOverlay::draw(sf::RenderWindow &window) const { window.draw(m_text); }
|
||||
|
||||
@@ -7,11 +7,12 @@
|
||||
|
||||
class DebugOverlay {
|
||||
public:
|
||||
DebugOverlay(const std::string& fontPath);
|
||||
explicit DebugOverlay(const std::string &fontPath);
|
||||
|
||||
void update(int drawCalls, float timeScale, sf::RenderWindow& window,
|
||||
void update(int drawCalls, float timeScale, const sf::RenderWindow &window,
|
||||
size_t threadCount, size_t ballsPerThread);
|
||||
void draw(sf::RenderWindow& window);
|
||||
|
||||
void draw(sf::RenderWindow &window) const;
|
||||
|
||||
private:
|
||||
sf::Font m_font;
|
||||
|
||||
+80
-73
@@ -4,24 +4,25 @@
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "BallFactory.hpp"
|
||||
#include "Constants.hpp"
|
||||
#include "DebugDraw.hpp"
|
||||
#include "ThreadUtils.hpp"
|
||||
#include "VectorMath.hpp"
|
||||
|
||||
Game::Game()
|
||||
: m_inputManager(
|
||||
std::bind(&Game::processKeyPressed, this, std::placeholders::_1),
|
||||
std::bind(&Game::processMousePressed, this, std::placeholders::_1)),
|
||||
[this](auto &&PH1) {
|
||||
processKeyPressed(std::forward<decltype(PH1)>(PH1));
|
||||
},
|
||||
[this](auto &&PH1) {
|
||||
processMousePressed(std::forward<decltype(PH1)>(PH1));
|
||||
}),
|
||||
m_debugLines(sf::PrimitiveType::Lines),
|
||||
m_drawCallCount(0),
|
||||
m_debugOverlay("assets/consolas.ttf"),
|
||||
m_threadPool(std::max(1u, std::thread::hardware_concurrency() > 2
|
||||
? std::thread::hardware_concurrency() - 2
|
||||
: 1u)) {
|
||||
m_threadPool(ThreadUtils::calculateSafeWorkerThreads()) {
|
||||
m_window.create(sf::VideoMode({Constants::WIDTH, Constants::HEIGHT}),
|
||||
"SFML Playground");
|
||||
m_window.setVerticalSyncEnabled(true);
|
||||
@@ -34,37 +35,46 @@ Game::Game()
|
||||
}
|
||||
}
|
||||
|
||||
void Game::processKeyPressed(const sf::Event::KeyPressed &kP) {
|
||||
if (kP.code == sf::Keyboard::Key::Equal) {
|
||||
m_timeScale += 0.25f;
|
||||
if (m_timeScale > 10.0f) m_timeScale = 10.0f;
|
||||
} else if (kP.code == sf::Keyboard::Key::Hyphen) {
|
||||
m_timeScale -= 0.25f;
|
||||
if (m_timeScale < 0.25f) m_timeScale = 0.25f;
|
||||
}
|
||||
|
||||
if (kP.code == sf::Keyboard::Key::D) {
|
||||
void Game::processKeyPressed(const sf::Event::KeyPressed &keyPressed) {
|
||||
switch (keyPressed.code) {
|
||||
case sf::Keyboard::Key::Equal:
|
||||
m_timeScale = std::min(10.0f, m_timeScale + 0.25f);
|
||||
break;
|
||||
case sf::Keyboard::Key::Hyphen:
|
||||
m_timeScale = std::max(0.25f, m_timeScale - 0.25f);
|
||||
break;
|
||||
case sf::Keyboard::Key::D:
|
||||
m_toggleDebug = !m_toggleDebug;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Game::processMousePressed(const sf::Event::MouseButtonPressed &mP) {
|
||||
sf::Vector2f mousePos(static_cast<float>(mP.position.x),
|
||||
static_cast<float>(mP.position.y));
|
||||
if (mP.button == sf::Mouse::Button::Left) impulseBalls(mousePos);
|
||||
if (mP.button == sf::Mouse::Button::Right) spawnBall(mousePos);
|
||||
void Game::processMousePressed(
|
||||
const sf::Event::MouseButtonPressed &mousePressed) {
|
||||
const sf::Vector2f mousePos(static_cast<float>(mousePressed.position.x),
|
||||
static_cast<float>(mousePressed.position.y));
|
||||
switch (mousePressed.button) {
|
||||
case sf::Mouse::Button::Left:
|
||||
impulseBalls(mousePos);
|
||||
break;
|
||||
case sf::Mouse::Button::Right:
|
||||
spawnBall(mousePos);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Game::spawnBall(const sf::Vector2f &mousePos) {
|
||||
auto newBallPtr = std::make_unique<Ball>(
|
||||
Constants::BALL_RADIUS,
|
||||
mousePos,
|
||||
sf::Vector2f(0.f, 0.f),
|
||||
sf::Color::Black);
|
||||
auto newBallPtr =
|
||||
std::make_unique<Ball>(Constants::BALL_RADIUS, mousePos,
|
||||
sf::Vector2f(0.f, 0.f), sf::Color::Black);
|
||||
m_objects.push_back(std::move(newBallPtr));
|
||||
}
|
||||
|
||||
void Game::impulseBalls(const sf::Vector2f &mousePos) {
|
||||
void Game::impulseBalls(const sf::Vector2f &mousePos) const {
|
||||
for (auto &object : m_objects) {
|
||||
auto *ball = dynamic_cast<Ball *>(object.get());
|
||||
if (!ball) continue;
|
||||
@@ -74,20 +84,21 @@ void Game::impulseBalls(const sf::Vector2f &mousePos) {
|
||||
}
|
||||
}
|
||||
|
||||
Game::Grid Game::buildSpatialGrid() {
|
||||
const float cellSize = 2 * Constants::BALL_RADIUS;
|
||||
const float safeCellSize = std::max(cellSize, 0.001f);
|
||||
Game::Grid Game::buildSpatialGrid() const {
|
||||
constexpr float safeCellSize = std::max(2 * Constants::BALL_RADIUS, 0.001f);
|
||||
Grid grid;
|
||||
|
||||
for (auto &object : m_objects) {
|
||||
if (Ball *ball = dynamic_cast<Ball *>(object.get())) {
|
||||
auto ball = dynamic_cast<Ball *>(object.get());
|
||||
if (!ball) continue;
|
||||
|
||||
const sf::Vector2f &pos = ball->getPosition();
|
||||
if (std::isfinite(pos.x) && std::isfinite(pos.y)) {
|
||||
if (!std::isfinite(pos.x) || !std::isfinite(pos.y)) continue;
|
||||
|
||||
int cellX = static_cast<int>(std::floor(pos.x / safeCellSize));
|
||||
int cellY = static_cast<int>(std::floor(pos.y / safeCellSize));
|
||||
grid[{cellX, cellY}].push_back(ball);
|
||||
}
|
||||
}
|
||||
}
|
||||
return grid;
|
||||
}
|
||||
|
||||
@@ -99,23 +110,23 @@ void Game::resolveSpatialCollisionsParallel(const Grid &grid) {
|
||||
std::mutex collisionsMutex;
|
||||
|
||||
for (const auto &[cell, cellBalls] : grid) {
|
||||
for (const auto &offset : forwardNeighbors) {
|
||||
Cell neighborCell = {cell.first + offset.first,
|
||||
cell.second + offset.second};
|
||||
for (const auto &[firstOffset, secondOffset] : forwardNeighbors) {
|
||||
Cell neighborCell = {cell.first + firstOffset,
|
||||
cell.second + secondOffset};
|
||||
auto neighborIt = grid.find(neighborCell);
|
||||
if (neighborIt == grid.end()) continue;
|
||||
|
||||
if (neighborCell == cell) {
|
||||
for (size_t i = 0; i < cellBalls.size(); ++i) {
|
||||
for (size_t j = i + 1; j < cellBalls.size(); ++j) {
|
||||
std::lock_guard<std::mutex> lock(collisionsMutex);
|
||||
std::lock_guard lock(collisionsMutex);
|
||||
collisionPairs.emplace_back(cellBalls[i], cellBalls[j]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (Ball *ballA : cellBalls) {
|
||||
for (Ball *ballB : neighborIt->second) {
|
||||
std::lock_guard<std::mutex> lock(collisionsMutex);
|
||||
std::lock_guard lock(collisionsMutex);
|
||||
collisionPairs.emplace_back(ballA, ballB);
|
||||
}
|
||||
}
|
||||
@@ -123,14 +134,15 @@ void Game::resolveSpatialCollisionsParallel(const Grid &grid) {
|
||||
}
|
||||
}
|
||||
|
||||
const size_t chunkSize = std::max(
|
||||
size_t(1), collisionPairs.size() / std::thread::hardware_concurrency());
|
||||
const size_t chunkSize =
|
||||
std::max(static_cast<size_t>(1),
|
||||
collisionPairs.size() / std::thread::hardware_concurrency());
|
||||
std::vector<std::future<void>> futures;
|
||||
|
||||
for (size_t i = 0; i < collisionPairs.size(); i += chunkSize) {
|
||||
size_t end = std::min(i + chunkSize, collisionPairs.size());
|
||||
|
||||
futures.push_back(m_threadPool.enqueue([&collisionPairs, i, end]() {
|
||||
futures.push_back(m_threadPool.enqueue([&collisionPairs, i, end] {
|
||||
for (size_t j = i; j < end; ++j) {
|
||||
auto &[ballA, ballB] = collisionPairs[j];
|
||||
ballA->resolveCollision(*ballB);
|
||||
@@ -138,9 +150,7 @@ void Game::resolveSpatialCollisionsParallel(const Grid &grid) {
|
||||
}));
|
||||
}
|
||||
|
||||
for (auto &future : futures) {
|
||||
future.get();
|
||||
}
|
||||
for (auto &future : futures) future.get();
|
||||
}
|
||||
|
||||
void Game::update() {
|
||||
@@ -148,22 +158,20 @@ void Game::update() {
|
||||
if (dt > 0.1f) dt = 0.1f;
|
||||
|
||||
updateBallsParallel(dt);
|
||||
|
||||
auto grid = buildSpatialGrid();
|
||||
|
||||
resolveSpatialCollisionsParallel(grid);
|
||||
resolveSpatialCollisionsParallel(buildSpatialGrid());
|
||||
}
|
||||
|
||||
void Game::updateBallsParallel(float dt) {
|
||||
const size_t chunkSize = std::max(
|
||||
size_t(1), m_objects.size() / std::thread::hardware_concurrency());
|
||||
const size_t chunkSize =
|
||||
std::max(static_cast<size_t>(1),
|
||||
m_objects.size() / std::thread::hardware_concurrency());
|
||||
|
||||
std::vector<std::future<void>> futures;
|
||||
|
||||
for (size_t i = 0; i < m_objects.size(); i += chunkSize) {
|
||||
size_t end = std::min(i + chunkSize, m_objects.size());
|
||||
|
||||
futures.push_back(m_threadPool.enqueue([&, i, end, dt]() {
|
||||
futures.push_back(m_threadPool.enqueue([&, i, end, dt] {
|
||||
for (size_t j = i; j < end; ++j) {
|
||||
if (auto *ball = dynamic_cast<Ball *>(m_objects[j].get())) {
|
||||
ball->update(dt, m_windowSize);
|
||||
@@ -174,24 +182,20 @@ void Game::updateBallsParallel(float dt) {
|
||||
}));
|
||||
}
|
||||
|
||||
for (auto &future : futures) {
|
||||
future.get();
|
||||
}
|
||||
for (auto &future : futures) future.get();
|
||||
}
|
||||
|
||||
void Game::render() {
|
||||
m_window.clear(sf::Color::Black);
|
||||
|
||||
m_debugLines.clear();
|
||||
m_drawCallCount = 0;
|
||||
|
||||
m_batchRenderer.clear();
|
||||
|
||||
std::vector<const Ball *> balls;
|
||||
balls.reserve(m_objects.size());
|
||||
|
||||
for (const auto &object : m_objects) {
|
||||
if (auto *ball = dynamic_cast<Ball *>(object.get())) {
|
||||
if (const auto *ball = dynamic_cast<Ball *>(object.get())) {
|
||||
balls.push_back(ball);
|
||||
m_batchRenderer.addBall(*ball);
|
||||
} else {
|
||||
@@ -200,7 +204,7 @@ void Game::render() {
|
||||
}
|
||||
|
||||
if (!m_toggleDebug) continue;
|
||||
if (auto *ball = dynamic_cast<Ball *>(object.get())) {
|
||||
if (const auto *ball = dynamic_cast<Ball *>(object.get())) {
|
||||
DebugDraw::addDirectionLine(m_debugLines, ball, m_window);
|
||||
DebugDraw::addVelocityLine(m_debugLines, ball);
|
||||
}
|
||||
@@ -211,8 +215,8 @@ void Game::render() {
|
||||
|
||||
if (m_toggleDebug) {
|
||||
DebugDraw::drawBatchedLines(m_window, m_debugLines);
|
||||
size_t threadCount = m_threadPool.getThreadCount();
|
||||
size_t ballsPerThread = m_objects.size() / threadCount;
|
||||
const size_t threadCount = m_threadPool.getThreadCount();
|
||||
const size_t ballsPerThread = m_objects.size() / threadCount;
|
||||
m_debugOverlay.update(m_drawCallCount + 2, m_timeScale, m_window,
|
||||
threadCount, ballsPerThread);
|
||||
m_debugOverlay.draw(m_window);
|
||||
@@ -221,26 +225,29 @@ void Game::render() {
|
||||
m_window.display();
|
||||
}
|
||||
|
||||
void Game::run() {
|
||||
while (m_window.isOpen()) {
|
||||
while (const std::optional event = m_window.pollEvent()) {
|
||||
if (event->is<sf::Event::Closed>()) {
|
||||
void Game::handleEvent(const sf::Event &event) {
|
||||
if (event.is<sf::Event::Closed>()) {
|
||||
m_window.close();
|
||||
return;
|
||||
}
|
||||
if (const auto *resized = event->getIf<sf::Event::Resized>()) {
|
||||
sf::Vector2f position(0.f, 0.f);
|
||||
sf::Vector2f size(static_cast<float>(resized->size.x),
|
||||
if (const auto *resized = event.getIf<sf::Event::Resized>()) {
|
||||
constexpr sf::Vector2f position(0.f, 0.f);
|
||||
const sf::Vector2f size(static_cast<float>(resized->size.x),
|
||||
static_cast<float>(resized->size.y));
|
||||
sf::FloatRect visibleArea(position, size);
|
||||
const sf::FloatRect visibleArea(position, size);
|
||||
m_window.setView(sf::View(visibleArea));
|
||||
m_windowSize = size;
|
||||
}
|
||||
if (auto kP = event->getIf<sf::Event::KeyPressed>())
|
||||
if (const auto kP = event.getIf<sf::Event::KeyPressed>())
|
||||
processKeyPressed(*kP);
|
||||
if (auto mP = event->getIf<sf::Event::MouseButtonPressed>())
|
||||
if (const auto mP = event.getIf<sf::Event::MouseButtonPressed>())
|
||||
processMousePressed(*mP);
|
||||
}
|
||||
}
|
||||
|
||||
void Game::run() {
|
||||
while (m_window.isOpen()) {
|
||||
while (const std::optional event = m_window.pollEvent())
|
||||
handleEvent(*event);
|
||||
update();
|
||||
render();
|
||||
}
|
||||
|
||||
+23
-13
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
#include <SFML/Graphics/Font.hpp>
|
||||
#include <SFML/Graphics/RenderWindow.hpp>
|
||||
#include <SFML/Graphics/Text.hpp>
|
||||
#include <SFML/Graphics/VertexArray.hpp>
|
||||
#include <SFML/System/Clock.hpp>
|
||||
#include <SFML/System/Vector2.hpp>
|
||||
@@ -21,11 +20,10 @@ class Ball;
|
||||
class PhysicalObject;
|
||||
|
||||
class Game {
|
||||
private:
|
||||
float m_timeScale = 1.f;
|
||||
sf::RenderWindow m_window;
|
||||
sf::Vector2f m_windowSize;
|
||||
std::vector<std::unique_ptr<PhysicalObject>> m_objects;
|
||||
std::vector<std::unique_ptr<PhysicalObject> > m_objects;
|
||||
sf::Clock m_clock;
|
||||
InputManager m_inputManager;
|
||||
sf::VertexArray m_debugLines;
|
||||
@@ -36,27 +34,39 @@ class Game {
|
||||
ThreadPool m_threadPool;
|
||||
|
||||
struct CellHash {
|
||||
std::size_t operator()(const std::pair<int, int>& k) const {
|
||||
std::size_t operator()(const std::pair<int, int> &k) const {
|
||||
return static_cast<std::size_t>(k.first) * 73856093 ^
|
||||
static_cast<std::size_t>(k.second) * 19349663;
|
||||
}
|
||||
};
|
||||
|
||||
using Cell = std::pair<int, int>;
|
||||
using Grid = std::unordered_map<Cell, std::vector<Ball*>, CellHash>;
|
||||
using Grid = std::unordered_map<Cell, std::vector<Ball *>, CellHash>;
|
||||
|
||||
void processKeyPressed(const sf::Event::KeyPressed &keyPressed);
|
||||
|
||||
void processMousePressed(const sf::Event::MouseButtonPressed &mousePressed);
|
||||
|
||||
void handleEvent(const sf::Event &event);
|
||||
|
||||
void impulseBalls(const sf::Vector2f &mousePos) const;
|
||||
|
||||
void spawnBall(const sf::Vector2f &mousePos);
|
||||
|
||||
void processKeyPressed(const sf::Event::KeyPressed& keyPressed);
|
||||
void processMousePressed(const sf::Event::MouseButtonPressed& mousePressed);
|
||||
void impulseBalls(const sf::Vector2f& mousePos);
|
||||
void spawnBall(const sf::Vector2f& mousePos);
|
||||
void update();
|
||||
void render();
|
||||
void updateBallsParallel(float dt);
|
||||
void resolveSpatialCollisionsParallel(const Grid& grid);
|
||||
|
||||
Grid buildSpatialGrid();
|
||||
void render();
|
||||
|
||||
void updateBallsParallel(float dt);
|
||||
|
||||
void resolveSpatialCollisionsParallel(const Grid &grid);
|
||||
|
||||
Grid buildSpatialGrid() const;
|
||||
|
||||
public:
|
||||
Game();
|
||||
|
||||
void run();
|
||||
|
||||
size_t getThreadCount() const { return m_threadPool.getThreadCount(); }
|
||||
};
|
||||
|
||||
@@ -5,14 +5,15 @@
|
||||
InputManager::InputManager(KeyCallback keyCb, MouseCallback mouseCb)
|
||||
: m_keyCallback(std::move(keyCb)), m_mouseCallback(std::move(mouseCb)) {}
|
||||
|
||||
void InputManager::processEvents(sf::Window& window) {
|
||||
void InputManager::processEvents(sf::Window& window) const {
|
||||
while (const std::optional event = window.pollEvent()) {
|
||||
if (event->is<sf::Event::Closed>()) {
|
||||
window.close();
|
||||
return;
|
||||
}
|
||||
if (auto kP = event->getIf<sf::Event::KeyPressed>()) m_keyCallback(*kP);
|
||||
if (auto mP = event->getIf<sf::Event::MouseButtonPressed>())
|
||||
if (const auto kP = event->getIf<sf::Event::KeyPressed>())
|
||||
m_keyCallback(*kP);
|
||||
if (const auto mP = event->getIf<sf::Event::MouseButtonPressed>())
|
||||
m_mouseCallback(*mP);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ class InputManager {
|
||||
|
||||
InputManager(KeyCallback keyCb, MouseCallback mouseCb);
|
||||
|
||||
void processEvents(sf::Window& window);
|
||||
void processEvents(sf::Window& window) const;
|
||||
|
||||
private:
|
||||
KeyCallback m_keyCallback;
|
||||
|
||||
@@ -5,7 +5,10 @@
|
||||
class PhysicalObject {
|
||||
public:
|
||||
virtual ~PhysicalObject() = default;
|
||||
virtual void update(float dt, const sf::Vector2f& windowSize) = 0;
|
||||
virtual void draw(sf::RenderWindow& window) = 0;
|
||||
virtual void applyImpulse(const sf::Vector2f& impulse) = 0;
|
||||
|
||||
virtual void update(float dt, const sf::Vector2f &windowSize) = 0;
|
||||
|
||||
virtual void draw(sf::RenderWindow &window) = 0;
|
||||
|
||||
virtual void applyImpulse(const sf::Vector2f &impulse) = 0;
|
||||
};
|
||||
|
||||
+18
-19
@@ -10,34 +10,34 @@
|
||||
class ThreadPool {
|
||||
public:
|
||||
explicit ThreadPool(size_t numThreads);
|
||||
|
||||
~ThreadPool();
|
||||
size_t getThreadCount() const { return workers.size(); }
|
||||
|
||||
[[nodiscard]] size_t getThreadCount() const { return workers.size(); }
|
||||
|
||||
template <class F, class... Args>
|
||||
auto enqueue(F&& f, Args&&... args)
|
||||
-> std::future<typename std::invoke_result<F, Args...>::type>;
|
||||
auto enqueue(F &&f, Args &&...args)
|
||||
-> std::future<std::invoke_result_t<F, Args...> >;
|
||||
|
||||
private:
|
||||
std::vector<std::thread> workers;
|
||||
|
||||
std::queue<std::function<void()>> tasks;
|
||||
std::queue<std::function<void()> > tasks;
|
||||
|
||||
std::mutex queueMutex;
|
||||
std::condition_variable condition;
|
||||
bool stop;
|
||||
};
|
||||
|
||||
inline ThreadPool::ThreadPool(size_t numThreads) : stop(false) {
|
||||
inline ThreadPool::ThreadPool(const size_t numThreads) : stop(false) {
|
||||
for (size_t i = 0; i < numThreads; ++i) {
|
||||
workers.emplace_back([this] {
|
||||
while (true) {
|
||||
std::function<void()> task;
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(this->queueMutex);
|
||||
this->condition.wait(lock, [this] {
|
||||
return this->stop || !this->tasks.empty();
|
||||
});
|
||||
std::unique_lock lock(this->queueMutex);
|
||||
this->condition.wait(
|
||||
lock, [this] { return this->stop || !this->tasks.empty(); });
|
||||
|
||||
if (this->stop && this->tasks.empty()) return;
|
||||
|
||||
@@ -53,29 +53,28 @@ inline ThreadPool::ThreadPool(size_t numThreads) : stop(false) {
|
||||
|
||||
inline ThreadPool::~ThreadPool() {
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(queueMutex);
|
||||
std::unique_lock lock(queueMutex);
|
||||
stop = true;
|
||||
}
|
||||
condition.notify_all();
|
||||
|
||||
for (auto& worker : workers) worker.join();
|
||||
for (auto &worker : workers) worker.join();
|
||||
}
|
||||
|
||||
template <class F, class... Args>
|
||||
inline auto ThreadPool::enqueue(F&& f, Args&&... args)
|
||||
-> std::future<typename std::invoke_result<F, Args...>::type> {
|
||||
using return_type = typename std::invoke_result<F, Args...>::type;
|
||||
auto ThreadPool::enqueue(F &&f, Args &&...args)
|
||||
-> std::future<std::invoke_result_t<F, Args...> > {
|
||||
using return_type = std::invoke_result_t<F, Args...>;
|
||||
|
||||
auto task = std::make_shared<std::packaged_task<return_type()>>(
|
||||
auto task = std::make_shared<std::packaged_task<return_type()> >(
|
||||
std::bind(std::forward<F>(f), std::forward<Args>(args)...));
|
||||
|
||||
std::future<return_type> res = task->get_future();
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(queueMutex);
|
||||
std::unique_lock lock(queueMutex);
|
||||
if (stop) throw std::runtime_error("enqueue on stopped ThreadPool");
|
||||
|
||||
tasks.emplace([task]() { (*task)(); });
|
||||
tasks.emplace([task] { (*task)(); });
|
||||
}
|
||||
|
||||
condition.notify_one();
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
#include <thread>
|
||||
|
||||
namespace ThreadUtils {
|
||||
inline size_t calculateSafeWorkerThreads(const size_t reserve = 2) {
|
||||
const size_t total = std::thread::hardware_concurrency();
|
||||
return total > reserve ? total - reserve : 1;
|
||||
}
|
||||
} // namespace ThreadUtils
|
||||
+6
-6
@@ -3,15 +3,15 @@
|
||||
#include <cmath>
|
||||
|
||||
namespace VectorMath {
|
||||
float length(const sf::Vector2f& vector) {
|
||||
float length(const sf::Vector2f &vector) {
|
||||
return std::sqrt(vector.x * vector.x + vector.y * vector.y);
|
||||
}
|
||||
|
||||
sf::Vector2f normalize(const sf::Vector2f& vector) {
|
||||
float squaredLen = vector.x * vector.x + vector.y * vector.y;
|
||||
if (squaredLen > 0.0001f) {
|
||||
float invLen = 1.0f / std::sqrt(squaredLen);
|
||||
return sf::Vector2f(vector.x * invLen, vector.y * invLen);
|
||||
sf::Vector2f normalize(const sf::Vector2f &vector) {
|
||||
if (const float squaredLen = vector.x * vector.x + vector.y * vector.y;
|
||||
squaredLen > 0.0001f) {
|
||||
const float invLen = 1.0f / std::sqrt(squaredLen);
|
||||
return {vector.x * invLen, vector.y * invLen};
|
||||
}
|
||||
return vector;
|
||||
}
|
||||
|
||||
+3
-2
@@ -2,6 +2,7 @@
|
||||
#include <SFML/System/Vector2.hpp>
|
||||
|
||||
namespace VectorMath {
|
||||
float length(const sf::Vector2f& vector);
|
||||
sf::Vector2f normalize(const sf::Vector2f& vector);
|
||||
float length(const sf::Vector2f &vector);
|
||||
|
||||
sf::Vector2f normalize(const sf::Vector2f &vector);
|
||||
} // namespace VectorMath
|
||||
|
||||
Reference in New Issue
Block a user