Impressive library, really solid work with lot's of helpful examples. First of all I want apologize for the wall of text that emerged from describing my issue.
Having no background using physics engines on such a low level I have tried to understand the concepts and implementation over the last couple days. While I made quite some progress I'm still struggling a bit to integrate the character demo into my project.
The problem is that my character is sliding as if it was on ice. It has trouble to accelerate and stop movement once the movementDirection is set to (0,0). Collisions seem to work, jumping is a bit moon-like aswell though. Before preparing and pasting a lot of sample code that would basically be the demo itself, I''ll quickly describe my project and which modifications I have made. Maybe you have an idea on what it could be this already.
My project is a pretty simple server, running a fixed time per update loop with 60hz and a dt of 1/60f. It's using Lidgren for networking and Unity3D as the game client, which works fine so far. I have a server-side PlayerController class in which I put the CharacterInput code, this act's as a networked input receiver. As I don't want to permanently send the clients movementDirection, I send state changes of the movementDirection and have the server keep running the simulation and CharacterController.UpdateCharacterGoals() in the server update loop. That's all the reason I've split the input values from the actual CharacterController.UpdateCharacterGoals() call.
However, on the buttonDownEvent of WASD the game sends a packet to the server that contains the current movementDirection (x/y values, always -1/0/1 nothing in between), which is stored on my server-side PlayerController. On buttonUpEvent of WASD the game basically sends a stopMovement packet, which set's the PlayerControllers movementDirection to (0,0).
CharacterController.UpdateCharacterGoals() is called in the servers update loop, where CharacterController.UpdateCharacterGoals() takes the current movementDirection from it's associated PlayerController instead of having it passed as function arguments.
Those are the few modifications I made to implement it into my existing architecture. There was still a small issue left in CharacterMotionConstraint.tt:, where I had to replace "IConstraintDescription<<#=prefix#>CharacterMotionConstraint>" with "IOneBodyConstraintDescription<<#=prefix#>CharacterMotionConstraint>" to fix an issue in CharacterController.cs:755 :
Code: Select all
Code: character.MotionConstraintHandle = Simulation.Solver.Add(character.BodyHandle, ref pendingConstraint.Description);
Error: Argument 2 cannot be passed with "ref" (Having german error messages, hope that is the correct one in english)
The character demo works fine when compiling it on my machine (not even the issue with the CharacterMotionConstraint), all using the latest github master commit.
Edit #1: After further debugging I know a little bit more. The ice-issue has it's cause identified, so it's a "linear velocity decreasing to zero too slowly"-problem, which explains the moon-like jumping as well. After further checking, it seems like the simulation rate in the demo is depending on my frame rate, as per the comment in Demo.cs:
Code: Select all
//In the demos, we use one time step per frame. We don't bother modifying the physics time step duration for different monitors so different refresh rates
//change the rate of simulation. This doesn't actually change the result of the simulation, though, and the simplicity is a good fit for the demos.
This is how I implemented my update loop if that's important, but from the logs it seems to be doing what it should:
Code: Select all
private const int SECOND_AS_MS = 1000;
private float TickRate = 60;
private TickRateTime = SECOND_AS_MS / options.TickRate;
private void StartUpdateLoop()
{
double begin;
double elapsed;
double remaining;
while (true)
{
begin = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
Update();
elapsed = (DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond) - begin;
remaining = TickRateTime - elapsed;
Logger.Trace($"Tick duration: {elapsed} ms");
Logger.Trace($"Remaining to {TickRate} hz cap: {remaining} ms");
// Sleep until the next tick, but at least 1ms to not ramp up CPU usage
Thread.Sleep(Math.Max(1, (int)remaining));
}
}
public void Update()
{
Logger.Trace("Server Tick processing");
// First we read what ever inputs we have
ReadMessages();
foreach (KeyValuePair<long, Session> session in clients)
{
session.Value.playerController.Update(false, 0.01f);
}
// Then we process the physics
physicsEngine.Update(options.PhysicsEngineOptions.dt);
// Send playfield update messages
BroadcastPlayfield();
Logger.Trace("Server Tick finished");
}
Code: Select all
Input.GetAxis("Horizontal"); // Returns filtered/lerped values, not hard -1/0/1 values as naively assumed
Input.GetAxisRaw("Horizontal"); // This is what you're looking for in a case like mine, does infact return -1/0/1 without intermediate values
Cheers