mirror of
https://gitea.wildfiregames.com/0ad/0ad
synced 2026-06-16 13:23:56 -07:00
The main change is the introduction of a 'pushing pressure' counter on
units. This counter increases when units get pushed around, and
decreases over time. In essence, units under high pressure move slower &
are harder to push around.
The major effect is that units can now get bogged down when very dense
groups start colliding. This makes movement more realistic, makes unit
movement more 'chokepointy', and generally improves the mathematical
soundness of the system (lower values are easier to handle for our 200ms
turns).
Other changes:
- The logic to detect units crossing each other's path has been
reworked. Units that run towards each other should not more obviously
avoid each other.
- New parameters: 'Spread' is a measure of how strong the pushing effect
is based on distance. With the current settings, static-pushing is
rather 'on/off', whereas moving-pushing is more gradual (and thus the
max influence distance was increased when moving).
- Default values have been tweaked for lower overlap.
- Units only looked at other units within their grid region. This led to
overlap near grid-borders. Units now look at neighboring grid elements,
which largely removes this issue. While this may be slower, the
performance of pushing was largely negligible before, so it is unlikely
to become a main cause of lag (and overlap was generally disliked by
players).
- Units no longer orient in the direction of pushing, but instead keep
facing their target. This can look slightly odd under very heavy pushing
forces, but vastly improves behaviour of very slow units such as rams
(since they spend much less time turning around). As a side-effect,
clean up angle code following acc780bcbb .
Engine changes:
- Add a debug rendering mode at compile-time to help understand what is
happening.
- Make it possible to constexpr initialise fractional fixed numbers by
using FromFraction
The 'pressure' change was inspired by alre's suggestion at
https://wildfiregames.com/forum/topic/56436-for-a-better-unit-movement/#comment-461987
Refs #6127
Differential Revision: https://code.wildfiregames.com/D4439
This was SVN commit r26245.
166 lines
5.2 KiB
C++
166 lines
5.2 KiB
C++
/* Copyright (C) 2022 Wildfire Games.
|
|
* This file is part of 0 A.D.
|
|
*
|
|
* 0 A.D. is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* 0 A.D. is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#ifndef INCLUDED_CCMPUNITMOTIONMANAGER
|
|
#define INCLUDED_CCMPUNITMOTIONMANAGER
|
|
|
|
#include "simulation2/system/Component.h"
|
|
#include "ICmpUnitMotionManager.h"
|
|
|
|
#include "simulation2/MessageTypes.h"
|
|
#include "simulation2/components/ICmpTerrain.h"
|
|
#include "simulation2/helpers/Grid.h"
|
|
#include "simulation2/system/EntityMap.h"
|
|
|
|
class CCmpUnitMotion;
|
|
|
|
class CCmpUnitMotionManager : public ICmpUnitMotionManager
|
|
{
|
|
public:
|
|
static void ClassInit(CComponentManager& componentManager);
|
|
|
|
DEFAULT_COMPONENT_ALLOCATOR(UnitMotionManager)
|
|
|
|
/**
|
|
* Maximum value for pushing pressure.
|
|
*/
|
|
static constexpr int MAX_PRESSURE = 255;
|
|
|
|
// Persisted state for each unit.
|
|
struct MotionState
|
|
{
|
|
MotionState(ICmpPosition* cmpPos, CCmpUnitMotion* cmpMotion);
|
|
|
|
// Component references - these must be kept alive for the duration of motion.
|
|
// NB: this is generally a super dangerous thing to do,
|
|
// but the tight coupling with CCmpUnitMotion makes it workable.
|
|
// NB: this assumes that components do _not_ move in memory,
|
|
// which is currently a fair assumption but might change in the future.
|
|
ICmpPosition* cmpPosition;
|
|
CCmpUnitMotion* cmpUnitMotion;
|
|
|
|
// Position before units start moving
|
|
CFixedVector2D initialPos;
|
|
// Transient position during the movement.
|
|
CFixedVector2D pos;
|
|
|
|
// Accumulated "pushing" from nearby units.
|
|
CFixedVector2D push;
|
|
|
|
fixed speed;
|
|
|
|
fixed initialAngle;
|
|
fixed angle;
|
|
|
|
// Used for formations - units with the same control group won't push at a distance.
|
|
// (this is required because formations may be tight and large units may end up never settling.
|
|
entity_id_t controlGroup = INVALID_ENTITY;
|
|
|
|
// This is a ad-hoc counter to store under how much pushing 'pressure' an entity is.
|
|
// More pressure will slow the unit down and make it harder to push,
|
|
// which effectively bogs down groups of colliding units.
|
|
uint8_t pushingPressure = 0;
|
|
|
|
// Meta-flag -> this entity won't push nor be pushed.
|
|
// (used for entities that have their obstruction disabled).
|
|
bool ignore = false;
|
|
|
|
// If true, the entity needs to be handled during movement.
|
|
bool needUpdate = false;
|
|
|
|
bool wentStraight = false;
|
|
bool wasObstructed = false;
|
|
|
|
// Clone of the obstruction manager flag for efficiency
|
|
bool isMoving = false;
|
|
};
|
|
|
|
// "Template" state, not serialized (cannot be changed mid-game).
|
|
|
|
// The maximal distance at which units push each other is the combined unit clearances, multipled by this factor,
|
|
// itself pre-multiplied by the circle-square correction factor.
|
|
entity_pos_t m_PushingRadiusMultiplier;
|
|
// Additive modifiers to the maximum pushing distance for moving units and idle units respectively.
|
|
entity_pos_t m_MovingPushExtension;
|
|
entity_pos_t m_StaticPushExtension;
|
|
// Multiplier for the pushing 'spread'.
|
|
// This should be understand as the % of the maximum distance where pushing will be "in full force".
|
|
entity_pos_t m_MovingPushingSpread;
|
|
entity_pos_t m_StaticPushingSpread;
|
|
|
|
// Pushing forces below this value are ignored - this prevents units moving forever by very small increments.
|
|
entity_pos_t m_MinimalPushing;
|
|
|
|
// Multiplier for pushing pressure strength.
|
|
entity_pos_t m_PushingPressureStrength;
|
|
// Per-turn reduction in pushing pressure.
|
|
entity_pos_t m_PushingPressureDecay;
|
|
|
|
// These vectors are reconstructed on deserialization.
|
|
|
|
EntityMap<MotionState> m_Units;
|
|
EntityMap<MotionState> m_FormationControllers;
|
|
|
|
// Turn-local state below, not serialised.
|
|
|
|
Grid<std::vector<EntityMap<MotionState>::iterator>> m_MovingUnits;
|
|
bool m_ComputingMotion;
|
|
|
|
static std::string GetSchema()
|
|
{
|
|
return "<a:component type='system'/><empty/>";
|
|
}
|
|
|
|
virtual void Init(const CParamNode& UNUSED(paramNode));
|
|
|
|
virtual void Deinit()
|
|
{
|
|
}
|
|
|
|
virtual void Serialize(ISerializer& serialize);
|
|
virtual void Deserialize(const CParamNode& paramNode, IDeserializer& deserialize);
|
|
|
|
virtual void HandleMessage(const CMessage& msg, bool global);
|
|
|
|
virtual void Register(CCmpUnitMotion* component, entity_id_t ent, bool formationController);
|
|
virtual void Unregister(entity_id_t ent);
|
|
|
|
virtual bool ComputingMotion() const
|
|
{
|
|
return m_ComputingMotion;
|
|
}
|
|
|
|
virtual bool IsPushingActivated() const
|
|
{
|
|
return m_PushingRadiusMultiplier != entity_pos_t::Zero();
|
|
}
|
|
|
|
private:
|
|
void OnDeserialized();
|
|
void ResetSubdivisions();
|
|
void OnTurnStart();
|
|
|
|
void MoveUnits(fixed dt);
|
|
void MoveFormations(fixed dt);
|
|
void Move(EntityMap<MotionState>& ents, fixed dt);
|
|
|
|
void Push(EntityMap<MotionState>::value_type& a, EntityMap<MotionState>::value_type& b, fixed dt);
|
|
};
|
|
|
|
REGISTER_COMPONENT_TYPE(UnitMotionManager)
|
|
|
|
#endif // INCLUDED_CCMPUNITMOTIONMANAGER
|