Jump to content
  • Advertisement

Angus Hollands

  • Content Count

  • Joined

  • Last visited

Community Reputation

869 Good

About Angus Hollands

  • Rank

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

  1. Angus Hollands

    how to adjust client predicted time?

    I think we're looking at this slightly differently - The reason I mention using the RTT is to approximate the time difference. In practice however, the RTT varies with network conditions and is going to deviate by several ticks at times. I consider there to be several problems: As the network conditions evolve, the client will move further into the future or past relative to the server. The server needs at least (1+N) network tick interval's worth of events to account for changing latency in the connection (N>0) The time at which events were generated is useful so that the client can move forward / back in time Point 3. can be resolved by sending the current "server tick" that corresponds to the world state tick at time T of generating events, which does trust the client, but this happens with any method (using RTT, clock nudging), and just requires some sanity conditions on the server. Point 1. and 2. are related, and the server will occasionally need to stop and recover in the event that it runs out of inputs. One of the finer points is differentiating between latency variations and network tick intervals, which are intentional delays. In the case of clock nudging, it is important that the accepted lead time is significantly larger than the network tick interval. I never felt comfortable with how clock adjusting was discussed - It seems vulnerable to panicking when network conditions change sufficiently, and will vary with the network (obviously). I prefer to have the client running in the future, but with no explicit notion of "aiming to arrive on time", given that this really is just an estimate. Having the clock vary according to the network wasn't something I liked particularly. I realise that I've just moved the "problem" around, of course. This probably makes no sense. It's late.
  2. Angus Hollands

    how to adjust client predicted time?

    The way that I dealt with this is to maintain a server side estimate of client RTT, and use that for clock estimation on the server (e.g tick_cli = tick_srv - tick_rate * RTT/2) This meant I didn't explictly deal with the client clock for any timestamping information, and used an incrementing event number to ensure events were inserted into the event queue on time.    In short, I didn't manage the clock on the client, as I saw complications - you suddenly lose the guarantee of contiguous time, which I am not comfortable with.    The only reason for this clock variation is to ensure that the server itself receives events when it expects them, whereby it compares the tick values between server and client. Providing that you can guarantee a fixed tick rate, you aren't actually moving forward /back in time, however, it is just the network conditions changing. This is why I approached this with - build an event queue server side of N events, where N might be the uncertainty in the client-server time * tick rate. This simply ensures the server has enough events that it never "runs out" of events to process to move the client forward in time. The RTT estimate that the server maintains is then used to resolve things like "was i shot, according to lag compensation". Otherwise, you could likely use some kind of sliding window smoothing, or 
  3. Simple answer:  Don't allow different simulation rates. Update the simulation at the same frequency on all players. Render at a different framerate to suit the player.   Complex answer: Use timestamps instead of ticks, and introduce time into all input values.   The simple answer is both simpler, more predictable and a whole lot more reliable. It's also quicker as you're not introducing lerps and other tolerance based comparisons where timestamps don't match exactly.   The important point is that you need to decouple the rendering framerate, and the simulation tick rate. You can render the game at a different framerate to the update.   If you set a baseline minimum for the update frequency, clients who can render at a higher framerate than the simulation rate can interpolate between the most recent states. This does mean you're rendering in the past by one-two simulation ticks, but that is usually acceptable, as you would otherwise be rendering the old frame anyway, and the effect is to reduce jagged movement, which will improve the UX.   I tried doing things using timestamps and variable simulation rates. It doesn't make any sense. You gain nothing by doing it, but a whole host of problems. Something about your game needs to be predictable and constant. That is the simulation. The rendering can happen at any rate, and the client will still move predictably and have a predictable experience across hardware.   http://gafferongames.com/game-physics/fix-your-timestep/
  4. There are several issues at play here.   Firstly, you shouldn't really be running at 500FPS simulation framerate. That's absurd! Most games with any physics will ask for at most 60FPS. You should lock your simulation frequency to something like 60Hz, and render separately to that timestep.   Secondly, you can't hope to send data every tick. You'll flood the connection. Running at a reduced transmission rate, and keeping packets small reduces the liklihood of this happening.   Thirdly, the speed at which information can travel between two end points is finite and non-zero. At best, it's the speed of light distance, realistically though the path between two nodes is not a geometric minimum, and isn't entirely vacuum (instead copper + fibre). In short, there will be latency, and it's calculable for a ballpark figure.   The clients do not need "new" state for each sim tick. You can interpolate between recent states, or extrapolate the latest state to smooth the timestep between two simulation states from the server.
  5. Whatever solution you use will depend on a few things, including whether your client simulation runs in the past or not. If you're already interpolating entities, that will give you some additional time to receive information about projectiles.   When it comes to entity replication, I will send a reliable un-ordered packet that contains the information required to instantiate the entity client-side. From there, I would send state updates as unreliable, discarding out-of-date packets. You could even reduce bandwidth for predictable projectiles and just send the initial state, with the network tick and velocity, and allow the client to simulate it.   I'm generally uncomfortable with the notion of reliable ordered. The reason most use UDP for fast-paced games is to avoid the latency that is fundamental to the operation of a reliable stream over unreliable network conditions. At least with reliable unordered you can proceed other state whilst the packet is redelivered when dropped.
  6. Angus Hollands

    Car transmission model

    Indeed, the torque curve is defined as follows (see the BoxsterS2004 class) maxTorqueArray = [220, 220, 310, 220] rpmAtMaxTorque = [0, 1000, 4500, 6600] Where maxTorqueArray are the Y values for the rpmAtMaxTorque X values.   I can see that the descending gear ratios will shift the engine back into the max torque region, but the applied force is reduced because the transmission factor (gear * differential * loss factor) is reduced by the gear reduction.  From this, I am led to believe that the model is fine, but good data is required.
  7. Angus Hollands

    Car transmission model

    Hi everyone,    I'm reviewing a car transmission model for a basic racing game, and I've noticed a slight discrepancy between what I would expect of the macroscopic properties of the model (when changing gear) and the numerical results.   Here is the basic model: from math import pi import numpy as np class Car: gearRatios = [] maxTorqueArray = [] rpmAtMaxTorque = [] wheelRadius = 0.0 differentialRatio = 0.0 Cd = 0.0 Crr = 0.0 transmissionEfficiency = 0.0 mass = 0 def __init__(self): self.speed = 0.0 self.acceleration = 0.0 self.currentGear = 1 def update(self, dt): throttle = 1.0 # Gear ratios assert self.currentGear > 0 currentGearRatio = self.gearRatios[self.currentGear - 1] effectiveGearRatio = self.differentialRatio * currentGearRatio transmission = effectiveGearRatio * self.transmissionEfficiency wheelCircumference = 2 * pi * self.wheelRadius # Find RPM wheelRPM = (self.speed / wheelCircumference) * 60 engineRPM = wheelRPM * transmission engineRPM = min(max(engineRPM, self.rpmAtMaxTorque[0]), self.rpmAtMaxTorque[-1]) # Find torque at RPM maxTorque = np.interp(engineRPM, self.rpmAtMaxTorque, self.maxTorqueArray) engineTorque = maxTorque * throttle wheelTorque = engineTorque * transmission tractionForce = wheelTorque / self.wheelRadius # Resolve forces dragForce = -self.Cd * self.speed * abs(self.speed) rollingResistanceForce = -self.Cd * 30 * self.speed # Resultant resultantForce = tractionForce + dragForce + rollingResistanceForce self.acceleration = resultantForce / self.mass self.speed += self.acceleration * dt self.currentForce = resultantForce self.currentRPM = engineRPM self.currentSpeed = self.speed self.currentTorque = wheelTorque class BoxsterS2004(Car): gearRatios = [3.82, 2.20, 1.52, 1.22, 1.02, 0.84] maxTorqueArray = [220, 220, 310, 220] rpmAtMaxTorque = [0, 1000, 4500, 6600] wheelRadius = 0.345 differentialRatio = 3.42 Cd = 0.32 transmissionEfficiency = 0.7 mass = 1300 if __name__ == "__main__": car = BoxsterS2004() dt = 1 / 60 while True: car.update(dt) Now, in it's actual implementation, I'm running the update function in a fixed-step game loop, which allows me to inspect the car state and shift gear at runtime.   Now, the behaviour: It is to be expected that the force of the car drops according to the speed for the same power. It is also to be expected that the car cannot reach as high a speed in lower gears.   Yet, for these parameters, the top speed can only be reached in 1st gear, and decelerates according as the gears are shifted higher. I'm not sure why this happens. It makes sense that the acceleration rate is lesser for higher speeds, because the drag force is proportional to v^2, and the force of the engine falls with higher gears, but my resultant forces are negative for higher gears when they're not for the lower gears. Shifting at any point reduces the max speed achievable.   Evidently, I don't quite understand the model well enough, could anyone point me in the right direction? To my mind, given that the driving force of the car falls off at 
  8. Angus Hollands

    Glitches in Navigating my NavMesh

    My experience with a range of techniques in this field is limited at the moment. I'm actually revisiting some pathfinding-systems I wrote in the past, which is why it's topical :) NB, Mikko Mononen, the author or Recast & Detour, wrote some articles on his blog about general pathfinding & following concepts. At the moment, I've left this in my own works, because I'm trying to get more performance out of the general navigation code (it seems that the funnel algorithm is running slowly for me, time to dig out the profiler!). However, local steering behaviours are the basis for collision avoidance, and there are a few great articles on digestingduck (the aforementioned blog) that describe how he does this. There are certainly other methods, though I will note that I like his approach to problem solving, which doesn't settle for cornercases. 
  9. Angus Hollands

    Glitches in Navigating my NavMesh

    The way that Recast & Detour handles this is to include the agent's radius in the navmesh (to avoid having to post-process paths). In my game, I just apply velocity in the bearing of the waypoint until within a specific margin (0.01 m or so (> than 1 frame velocity error)) and then switch to the next waypoint. You could also check if the dot of the vector from the current waypoint to the npc position is greater than the distance between the current and next waypoint. I avoid recalculating paths by only doing so if the target moves. You could extend this further by keeping track of entites within the same node and checking if they're in the path or not. To handle the case where the player leaves the navmesh, I keep track of the last node the player was within, then find the nearest neighbour according to the edge distance (assuming that I'm not currently in a node).
  10. Angus Hollands

    Simplify A* path

    The funnel algorithm essentially traces the view of the agent as it attempts to follow the A* path. I have an example implementation here if you care to look at it.  The example on Mikko Mononen's blog may also be useful.
  11. Angus Hollands

    Client side prediction

    There are a few things that you must be careful of. My game movement code, for the record, looks like this: https://github.com/agoose77/PyAuthServer/blob/master/game_system/controllers.py (PlayerPawnController)   The client creates a move: Applies inputs Saves inputs, Processes physics Saves physics state Creates a struct of the physics state and inputs state (so that the physics state is the results of those inputs)  And sends it to the server. The server buffers this move (a queue, basically).   In the update tick, the server pops a move from the queue, and processes it. The queue is artificially delayed so that we have at least 100ms (or arbitrary time) of input data incase of jitter (variable latency). This problem (which you may have overlooked) means that the server won't receive moves regularly, which means it will think the client mis-simulated certain moves (if you send the correct position from the client, but the server disagrees because It didn't simulate a movement for a specific tick).   The server then uses this move to process client inputs and compare the results of the simulation to the client. If the client didn't produce similar results (of both rotation and location) (not dynamics, as errors will be seen in the location/rotation later on), we send a correction to the client. The client will be X ticks ahead now (RTT + server buffer latency) and so will lookup the old move, and resimulate the inputs from this move. At this point you must also consider the fact that once you make a correction for move T, there will most likely be corrections for another [RTT / 2 + server buffer latency] ticks, because it still receives old moves "in the wire" and in the buffer. So, you should ignore any corrections until this time has elapsed. The safest way to do this is have the client save the current outgoing move ID when it makes a correction, so that later corrections from the server are rejected if their ID is less than the ID which includes the correction. Alternatively, (as seen in my code, though I'll change this), just have the server send one correction (the full state), and wait for the client to tell it that it has received that correction. Due to the possibility of variable packed order, this is less of  a good solution.   You should also handle what happens if a move isn't received between received moves (T arrives, T + 1 arrives, T + 2 doesn't arrive, T+3 arrives ...). Because of the RTT, re-sending doesn't make sense; it won't arrive in time, so often the best solution is sending moves redundantly.
  12. I think the general argument in favour of UE4 Is pretty well founded. It's a good engine, and the Blueprints system seems to be meshing well with non-programmers. I would encourage investing some initial time in familiarising the participants with the fundamentals of programming (in this case, visual programming) in order to enabe them to embrace the mindset that is required to problem-solve with programming tools.   In the interests of being open, there is also the Blender Game Engine. It has a some-what restrictive visual programming tool called "Logic Bricks". It has a quasi-nodal interface, with the restriction that there are three columns for sensors, controllers and actuators, and each may only trigger left to right. Essentially it's a source - evaluation - action system. It is possible to run Python as a controller if more advanced evaluation is required (or use an Expression controller which allows basic boolean operators and property (attribute) comparisons).   The biggest benefit of the engine is that it is embedded inside of Blender, a 3D modelling, animation, rendering, simulation, compositing ... program, which means it is very quick to import models, textures and other materials into the game, and no code nor export pipeline is required.
  13. Angus Hollands

    GUI liblary C++

    One interesting approach that I'd not seen very much of recently is to use a web-renderer. Websites are built for information presentation, and have a vast array of libraries in js and jquery for adding interactive elements. Many basic features are already in place. Rendering to your game instance would make it very quick to support UI within the game, and very maintainable. 
  14. As you may be aware, there are a large number of different implementations of component based systems. Especially in languages such as C++, the benefits of ECS designs can be the ability to employ contiguous memory. If you have each system allocate a pool of components, then you can reduce the frequency of cache misses, and hence improve performance of system updates.   Hence, when you register a component with an entity, it makes sense to request it from a system, which will maintain an internal pool (and therefore update internally) of components. IComponent* physics_component = physics_system->create_component(); entity->components.push_back(physics_component); // Some time later double delta_time = 1.0/60; physics_system->update(delta_time); This has its difficulties when you have systems which share components with other systems. Generally speaking, avoiding this dependency helps a great deal.
  15. Angus Hollands

    Why is scaling an mmo so difficult?

    That's exactly it! Now, good luck developing! The tricky part is in the how - how do we "share load" between servers, in a seamless fashion to the end user? How to we divide the calculations required across multiple servers? Do we split them geographically in the real world (for faster latency response) or in world (for faster lookup of local entity data)? How should we handle interactions across servers?   I'm not an MMO specialist, but there are a number of topics recently posted in this forum which should be of use :)
  • Advertisement

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!