- Home /
What is the best way to implement physic simulation prediction for AI in Unity?
I develop top-down 2d cars battle game. Cars has rather complex physics, with simulation of each wheel direct and side resistances, smooth contoled gas and steering wheels rotation. Additionaly, physics may be changed for game design purposes.... And, yeap, now I need to write AI for cars control.
.
To predicate car physics behaviour for low level AI (low level means simple actions control, like orienting to target rotation), I tried to find precise solution of car movement differential equation. But, unfortunately, it looks like my math skills are not so perfect to do it.
Currently I see next acceptable options: use some kind of heuristic algorithm (genetic algorithms, deep learing and so on) or just simulate several variants of car control in next frames and select the most suitable.
.
My expirience in heuristic algorithm is limited university studies, so I decided to use simulation (furthermore, simulation will be needed for heuristic algorithm too). And, finally, my question... What is the best way to create very simple physics scene separated from main scene that will be used only for simple simulation of rigid bodies future behviour for AI? Each AI will use body on this scene to setup this AI's car physic state, perform a few dozens of simulation steps and decide what control variant is the best to achieve needed state.
.
I found Separating Physics Scenes topic on Unity forum and thought that this is what I need. But then I read SceneManager.CreateScene documation and next sentence made me doubt: "The new Scene will be opened additively into the hierarchy alongside any existing Scenes that are currently open". Will be the physics of new scene separated from game scene? Or there is some more correct way to achieve needed result?
.
Sorry about so lot of words... But I think this topic will be interesting not only for me. A lot of games using physic-based gameplay and most of them need AI for game units.
.
P.S.: I know, that there is simple solution - use simplified physics for drived by AI cars. I'll leave this option as a last solution if all other will be too complex for me.
.
UPDATE: I have just found forum topic ("Physics simulate for a single object possible") that seems confirming usage Separating Physics Scenes as solution of my task. I leave this question opened until testing and will publish result as answer (unless a more suitable solution appears).
Hey there,
tough topic you got there, not sure if you will get a decent answer for this. From my experience with ai behaviour i think your approach is a bit over the top high-techy. Sounds nice but for a setup that i encountered i simply used 2 raycasts and the relative player position to steer vehicles in 3d.(They have their issues -> do not recognize cliffs and will flip from time to time but that did not matter for me) That was enough to make it look nice. Players most likely will never notice any ultra sophisticated ai behavior (well except for the case that this is the main core of your game) So to keep it short: Try to keep it as simple as possible. Also makes debugging far easier. Hope this helps you on anyways...
Sorry, forgot to answer... Thank you for advice, but, unfortunately, it's impossible to select such simple solution in my case. Firstly, I have no track limited by walls. Secondly, AI should follow some moving object (for example, player's car), not just move through the track avoiding walls and other obstacles. When implementing pursuit for object with complex physics, microtasks that looks simple (like "rotate until you are not looking to pursuited object") transforms to complex one. Simulation make possible to test a number of standard maneuvers and select next AI action based on the most suitable simulation result.
Answer by vsemenyakin · Mar 17, 2019 at 08:49 AM
Finally, I can answer for my question) I created two small code examples of using 2d physics simulation.
.
Repo link
.
In first example we create separated scene with 2d physics, create GameObject with collider+rigid body on active (main/default) scene, move this object to created simulation scene, launch this object by starting force and perform 50 physics steps of gravity simulation drawing the result of each step. This example is extrimly simple and, I think, should be simple to understand without other explenation.
.
using UnityEngine;
using UnityEngine.SceneManagement;
public class VerySimpleSimulationTest : MonoBehaviour
{
void Start() {
performSimpleFlyingInGravityTest();
}
void performSimpleFlyingInGravityTest()
{
//Create simulation scene
Scene theScene = SceneManager.CreateScene(
"PhysicsSimulation", /*Scene name*/
new CreateSceneParameters(LocalPhysicsMode.Physics2D) /*Type of physics on scene*/
);
PhysicsScene2D thePhysicsScene = theScene.GetPhysicsScene2D();
//Create object that will be simulated
GameObject theGameObject = new GameObject();
var theCollider = theGameObject.AddComponent<BoxCollider2D>();
var theRigidBody = theGameObject.AddComponent<Rigidbody2D>();
//Object is created in scene that is currently active.
// So we should move object to simulation scene using
// SceneManager.MoveGameObjectToScene(...) or
// set simulation scene as active using
// SceneManager.SetActiveScene(...)
SceneManager.MoveGameObjectToScene(theGameObject, theScene);
//Perform simulation
//-Give starting force
theRigidBody.AddForce(new Vector2(200.0f, 500.0f));
for (int theStepIndex = 0; theStepIndex < 50; ++theStepIndex)
{
//-Apply gravity (instead of default created physics
//- we need to apply it manually)
theRigidBody.AddForce(new Vector2(0.0f, -9.8f));
//-Draw current object position in simulation
drawSquare(theGameObject.transform.position, theCollider.size, Color.green, 10.0f);
//-Perform scene simulation step with delta time 0.02 seconds
thePhysicsScene.Simulate(0.02f);
}
}
static void drawSquare(Vector2 inPosition, Vector2 inSize, Color inColor, float inDuration) {
Vector2 theRectCornerPosition = inPosition;
theRectCornerPosition -= inSize/2;
Vector2 theLastRectCornerPosition = theRectCornerPosition;
theRectCornerPosition.x += inSize.x;
Debug.DrawLine(theLastRectCornerPosition, theRectCornerPosition, inColor, inDuration, false);
theLastRectCornerPosition = theRectCornerPosition;
theRectCornerPosition.y += inSize.y;
Debug.DrawLine(theLastRectCornerPosition, theRectCornerPosition, inColor, inDuration, false);
theLastRectCornerPosition = theRectCornerPosition;
theRectCornerPosition.x -= inSize.x;
Debug.DrawLine(theLastRectCornerPosition, theRectCornerPosition, inColor, inDuration, false);
theLastRectCornerPosition = theRectCornerPosition;
theRectCornerPosition.y -= inSize.y;
Debug.DrawLine(theLastRectCornerPosition, theRectCornerPosition, inColor, inDuration, false);
}
}
.
Second example makes approximly the same, but incapsulates simulated object logic to separate class and use implemented API to continue updates in FixedUpdate of main scene. We create separate scipt to hold object simulation specific logic. Pay an attantion how API of the simulated object changes for possibility to perform simulation independtly from any Updates: we create simulateStep() method for simulated object to perform actions that ordinary takes place in FixedUpdate() and call it manualy before all simulated physics scene simulation update.
.
using UnityEngine;
public class SimulatedObjectLogic : MonoBehaviour
{
private BoxCollider2D _collider = null;
private Rigidbody2D _rigidBody = null;
//API
public void launch(Vector2 inForce) {
getRigidbody().AddForce(inForce);
}
//Method that should be used insted FixedUpdate before simulated scene
// Simulate(...) call
public void simulateStep() {
getRigidbody().AddForce(new Vector2(0.0f, -9.8f));
}
public void draw() {
drawSquare(transform.position, getBoxCollider().size, Color.green, 10.0f);
}
//Implementation details
//NB: As we want to simulate object at start of the test the start
// of Simulated Object may be called to late. So it's good idea
// don't save references to components here, but use lazy accessors
// with caching of component references
//private void Start() {
// _collider = gameObject.GetComponent<BoxCollider2D>();
// _rigidBody = gameObject.GetComponent<Rigidbody2D>();
//}
// VS
private BoxCollider2D getBoxCollider() {
if (null == _collider) _collider = gameObject.GetComponent<BoxCollider2D>();
return _collider;
}
private Rigidbody2D getRigidbody() {
if (null == _rigidBody) _rigidBody = gameObject.GetComponent<Rigidbody2D>();
return _rigidBody;
}
//-Utils
static void drawSquare(Vector2 inPosition, Vector2 inSize, Color inColor, float inDuration)
{
Vector2 theRectCornerPosition = inPosition;
theRectCornerPosition -= inSize / 2;
Vector2 theLastRectCornerPosition = theRectCornerPosition;
theRectCornerPosition.x += inSize.x;
Debug.DrawLine(theLastRectCornerPosition, theRectCornerPosition, inColor, inDuration, false);
theLastRectCornerPosition = theRectCornerPosition;
theRectCornerPosition.y += inSize.y;
Debug.DrawLine(theLastRectCornerPosition, theRectCornerPosition, inColor, inDuration, false);
theLastRectCornerPosition = theRectCornerPosition;
theRectCornerPosition.x -= inSize.x;
Debug.DrawLine(theLastRectCornerPosition, theRectCornerPosition, inColor, inDuration, false);
theLastRectCornerPosition = theRectCornerPosition;
theRectCornerPosition.y -= inSize.y;
Debug.DrawLine(theLastRectCornerPosition, theRectCornerPosition, inColor, inDuration, false);
}
}
.
using UnityEngine;
using UnityEngine.SceneManagement;
public class SimpleSimulationTest : MonoBehaviour
{
public GameObject ObjectToSimulatePrefab = null;
PhysicsScene2D _physicsScene;
GameObject _objectToSimulte = null;
void Start() {
//Create simulation scene
Scene theScene = SceneManager.CreateScene(
"PhysicsSimulation",
new CreateSceneParameters(LocalPhysicsMode.Physics2D)
);
_physicsScene = theScene.GetPhysicsScene2D();
//Create object that will be simulated
_objectToSimulte = GameObject.Instantiate(ObjectToSimulatePrefab);
SceneManager.MoveGameObjectToScene(_objectToSimulte, theScene);
//Perform starting simulation
SimulatedObjectLogic theSimulatedObjectLogic =
_objectToSimulte.GetComponent<SimulatedObjectLogic>();
theSimulatedObjectLogic.launch(new Vector2(200.0f, 500.0f));
for (int theStepIndex = 0; theStepIndex < 50; ++theStepIndex)
{
theSimulatedObjectLogic.draw();
theSimulatedObjectLogic.simulateStep();
_physicsScene.Simulate(0.02f);
}
}
private void FixedUpdate() {
//Simulate next steps in main scene FixedUpdate
SimulatedObjectLogic theSimulatedObjectLogic =
_objectToSimulte.GetComponent<SimulatedObjectLogic>();
theSimulatedObjectLogic.draw();
theSimulatedObjectLogic.simulateStep();
_physicsScene.Simulate(0.02f);
}
}
.
P.S.: I have not a lot of experience in Unity - so may update this answer after discovering new details.
Your answer
Follow this Question
Related Questions
Dynamic colliders without Rigidbodies? 0 Answers
Maintaining the trajectory of a ball when manipulating its velocity in a Breakout/Arkanoid clone 1 Answer
Prevent reseting of GameObject physics state on SceneManager.MoveGameObjectToScene(...) 0 Answers
Rigidbody2D doesn't move 0 Answers
Move an object via Rigidbody2D Physics and Transform at same time 0 Answers