To practice AI, I decided to make a AI simulation game like Cuboid Sandbox. Like Cuboid Sandbox, it would have 3 teams of AI (however mine has red, green, and blue rather than red, yellow, and blue).
In Cuboid Sandbox, there was a max of 750 in total and mine has no limit (yet). Through testing, I've found that Cuboid Sandbox doesn't seem to have any drops in framerate no matter how many AI there currently is (using bandicam's framerate counter since there's none in the game so it may be slightly inaccurate although it never looked choppy/laggy).
And in mine, I start to drop to 40fps at around 100 AI in total.
I've tracked this down to where I update my food lists for the AI
Food is separated by each team (if a team has no food left, it goes to the nearest) and for each AI I set a list called 'availableFoodSources' to a list called 'redFood' that's set in the spawner function when it spawns the food on startup and only updates when a food source is destroyed or a new one is spawned.
At large amounts of AI this causes a lot of lag.
There's also another similar loop (removed due to immense amount of lag) where I check for if an enemy tribe distance is less than 20 by loop through all other AI tribes and checking the distance for each one.
I'm not sure how to tackle either of these issues.
Also I'm using StateMachineBehaviour in unity which removes my ability to (easily) use coroutines.
Here's the bit of code where I update availableFoodSources:
if (aIController.team == Color.red)
{
availableFoodSources = spawner.redFood;
}
if (aIController.team == Color.green)
{
availableFoodSources = spawner.greenFood;
}
if (aIController.team == Color.blue)
{
availableFoodSources = spawner.blueFood;
}
if (!Attacking)
{
if (animator.transform.GetComponent<AIController>().hunger > 30)
{
GameObject f = FindClosestFoodSource(animator.transform);
Vector3 closestFoodPos = f.transform.position;
Debug.DrawLine(animator.transform.position, closestFoodPos);
closestFoodPos.y = animator.transform.position.y;
animator.GetComponent<NavMeshAgent>().SetDestination(closestFoodPos);
}
else
{
animator.SetBool("Walking", false);
}
}
Related
I model a swing that has its own drive. This drive consists of a housing and a weight inside it. The weight is accelerated with an electromagnetic field, so that it hits the wall of the housing at high speed and thus sets the swing in motion.
I am farely new to unity but i thought i did everything correct. At the push of a button the weight was accelerated, hit the housing and the swing startet moving. It worked very well until i started to increase the force which is accelerating the weight (The weight is pretty small, so it needs a lot of speed to move the swing). Now the weight is flying out of the housing. I checked all collision boxes. They are correct and i even made them overlapping to ensure this is not the mistake. I have no idea how to fix this problem and would be grateful for any help.
Here is the code that accelerates the weight, in case you need it:
if (rightPressed)
{
GetComponent<Rigidbody>().AddForce(transform.right * forceSlider.value, ForceMode.Impulse);
rightPressed = false;
}
else if (leftPressed)
{
leftPressed = false;
GetComponent<Rigidbody>().AddForce(transform.right * -forceSlider.value, ForceMode.Impulse);
}
For fast moving objects make sure to set the Rigidbody.collisionDetectionMode to CollisionDetectionMode.ContinuousDynamic
I'm making an Asteroid Destroyer game in which you get score as time passes by (the game becomes progressively more difficult), but also when you hit an asteroid. I have written a method inside the Game1 class to calculate the amount of score that should be added by the end of the Update method.
The code is as follows:
private float CountScore(GameTime gameTime, List<Asteroid> asteroids)
{
float addScore = 0;
foreach (Asteroid asteroid in asteroids)
{
if (asteroid.Killed)
{
addScore += 100;
}
}
if (spaceship1.Alive)
{
addScore += 80 * (float)gameTime.ElapsedGameTime.TotalSeconds;
}
return addScore;
}
The method is called like this in the Update method:
score += CountScore(gameTime, asteroids);
The part of the code that adds score as time passes works fine. The problem is that when I destroy an asteroid, instead of getting 100 more score points, sometimes I get 200, sometimes I get 2000 and sometimes I get 5000. I don't understand what is happening, since only asteroids that are killed by the player get the property Killed = true;, so I don't really think it is adding 100 per asteroid on screen.
Does anybody know what could cause this? If it's needed I can post more code, but I didn't want to write too much here for the sake of readability.
Edit:
Adding asteroid.Killed = false; just after adding the score worked. However I still don't understand what was the issue, since it looks like it was looping through the same asteroid more than once.
I would recommend that the logic for adding to the score when an asteroid is killed, is done at the same time as the logic for killing an asteroid.
It is likely that you are iterating through the list of asteroids already when processing the hit detection, and thus it would improve performance if the logic was done at the same time, instead of having another foreach loop, as demonstrated in your code.
Further more your addScore += 80 * (float)gameTime.ElapsedGameTime.TotalSeconds; is unreliable, a player with a higher FPS will have an unfair advantage. I recommend using the deltaTime when adding to the total score during the games progression.
So, this seems to be a strange one (to me). I'm relatively new to Unity, so I'm sure this is me misunderstanding something.
I'm working on a VSEPR tutorial module in unity. VSEPR is the model by which electrons repel each other and form the shape (geometry) of atoms.
I'm simulating this by making (in this case) 4 rods (cylinder primitives) and using rigidbody.AddForce to apply an equal force against all of them. This works beautifully, as long as the force is equal. In the following image you'll see the rods are beautifully equi-distant at 109.47 degrees (actually you can "see" their attached objects, two lone pairs and two electron bonds...the rods are obscured in the atom shell.)
(BTW the atom's shell is just a sphere primitive - painted all pretty.)
HOWEVER, in the real world, the lone pairs actually exert SLIGHTLY more force...so when I add this additional force to the model...instead of just pushing the other electron rods a little farther away, it pushes the ENTIRE 4-bond structure outside the atom's shell.
THE REASON THIS SEEMS ODD IS...two things.
All the rods are children of the shell...so I thought that made them somewhat immobile compared to the shell (I.e. if they moved, the shell would move with them...which would be fine).
I have a ConfiguableJoint holding the rods to the center of the atoms shell ( 0,0,0). The x/y/z-motion is set to fixed. I thought this should keep the rods fairly immutably attached to the 0,0,0 center of the shell...but I guess not.
POINTS OF INTEREST:
The rods only push on each other, by a script attached to one rod adding force to an adjacent rod. they do not AddForce to the atom shell or anything else.
CODE:
void RepulseLike() {
if (control.disableRepulsionForce) { return; } // No force when dragging
//////////////////// DETERMINE IF FORCE APPLIED //////////////////////////////
// Scroll through each Collider that this.BondStick bumps
foreach (Collider found in Physics.OverlapSphere(transform.position, (1f))) {
// Don't repel self
if (found == this.collider) { continue; }
// Check for charged particle
if (found.gameObject.tag.IndexOf("Charge") < 0) { continue; }// No match "charge", not a charged particle
/////////////// APPLY FORCE ///////////////
// F = k(q1*q2/r^2)
// where
// k = Culombs constant which in this instance is represented by repulseChargeFactor
// r = distance
// q1 and q2 are the signed magnitudes of the charges, in this case -1 for electrons and +1 for protons
// Swap from local to global variable for other methods
other = found;
/////////////////////////////////
// Calculate pushPoints for SingleBonds
forceDirection = (other.transform.position - transform.position) * magnetism; //magnetism = 1
// F = k(q1*q2/distance^2), q1*q2 ia always one in this scenario. k is arbitrary in this scenario
force = control.repulseChargeFactor * (1 / (Mathf.Pow(distance, 2)));
found.rigidbody.AddForce(forceDirection.normalized * force);// * Time.fixedDeltaTime);
}//Foreach Collider
}//RepulseLike Method
You may want to use spheres to represent electrons, so apply forces onto them and re-orient(rotate) rods according to this sphere.
I think I found my own answer...unless someone has suggestions for a better way to handle this.
I simply reduced the mass of the electron rods, and increased the mass of the sphere. I'm not sure if this is the "best practices" solution or a kluge...so input still welcomed :-)
I am currently working on a project in C# where i play around with planetary gravitation, which i know is a hardcore topic to graps to it's fullest but i like challenges. I've been reading up on Newtons laws and Keplers Laws, but one thing i cannot figure out is how to get the correct gravitational direction.
In my example i only have 2 bodies. A Satellite and a Planet. This is to make is simplify it, so i can grasp it - but my plan is to have multiple objects that dynamically effect each other, and hopefully end up with a somewhat realistic multi-body system.
When you have an orbit, then the satellite has a gravitational force, and that is ofcourse in the direction of the planet, but that direction isn't a constant. To explain my problem better i'll try using an example:
let's say we have a satellite moving at a speed of 50 m/s and accelerates towards the planet at a speed of 10 m/s/s, in a radius of 100 m. (all theoretical numbers) If we then say that the framerate is at 1, then after one second the object will be 50 units forward and 10 units down.
As the satellite moves multiple units in a frame and about 50% of the radius, the gravitational direcion have shifted alot, during this frame, but the applied force have only been "downwards". this creates a big margin of error, especially if the object is moving a big percentage of the radius.
In our example we'd probably needed our graviational direction to be based upon the average between our current position and the position at the end of this frame.
How would one go about calculating this?
I have a basis understanding of trigonometry, but mainly with focus on triangles. Assume i am stupid, because compared to any of you, i probably am.
(I made a previous question but ended up deleting it as it created some hostility and was basicly not that well phrased, and was ALL to general - it wasn't really a specific question. i hope this is better. if not, then please inform me, i am here to learn :) )
Just for reference, this is the function i have right now for movement:
foreach (ExtTerBody OtherObject in UniverseController.CurrentUniverse.ExterTerBodies.Where(x => x != this))
{
double massOther = OtherObject.Mass;
double R = Vector2Math.Distance(Position, OtherObject.Position);
double V = (massOther) / Math.Pow(R,2) * UniverseController.DeltaTime;
Vector2 NonNormTwo = (OtherObject.Position - Position).Normalized() * V;
Vector2 NonNormDir = Velocity + NonNormTwo;
Velocity = NonNormDir;
Position += Velocity * Time.DeltaTime;
}
If i have phrased myself badly, please ask me to rephrase parts - English isn't my native language, and specific subjects can be hard to phrase, when you don't know the correct technical terms. :)
I have a hunch that this is covered in keplers second law, but if it is, then i'm not sure how to use it, as i don't understand his laws to the fullest.
Thank you for your time - it means alot!
(also if anyone see multi mistakes in my function, then please point them out!)
I am currently working on a project in C# where i play around with planetary gravitation
This is a fun way to learn simulation techniques, programming and physics at the same time.
One thing I cannot figure out is how to get the correct gravitational direction.
I assume that you are not trying to simulate relativistic gravitation. The Earth isn't in orbit around the Sun, the Earth is in orbit around where the sun was eight minutes ago. Correcting for the fact that gravitation is not instantaneous can be difficult. (UPDATE: According to commentary this is incorrect. What do I know; I stopped taking physics after second year Newtonian dynamics and have only the vaguest understanding of tensor calculus.)
You'll do best at this early stage to assume that the gravitational force is instantaneous and that planets are points with all their mass at the center. The gravitational force vector is a straight line from one point to another.
Let's say we have a satellite moving at a speed of 50 m/s ... If we then say that the framerate is one frame per second then after one second the object will be 50 units right and 10 units down.
Let's make that more clear. Force is equal to mass times acceleration. You work out the force between the bodies. You know their masses, so you now know the acceleration of each body. Each body has a position and a velocity. The acceleration changes the velocity. The velocity changes the position. So if the particle starts off having a velocity of 50 m/s to the left and 0 m/s down, and then you apply a force that accelerates it by 10 m/s/s down, then we can work out the change to the velocity, and then the change to the position. As you note, at the end of that second the position and the velocity will have both changed by a huge amount compared to their existing magnitudes.
As the satellite moves multiple units in a frame and about 50% of the radius, the gravitational direcion have shifted alot, during this frame, but the applied force have only been "downwards". this creates a big margin of error, especially if the object is moving a big percentage of the radius.
Correct. The problem is that the frame rate is enormously too low to correctly model the interaction you're describing. You need to be running the simulation so that you're looking at tenths, hundredths or thousanths of seconds if the objects are changing direction that rapidly. The size of the time step is usually called the "delta t" of the simulation, and yours is way too large.
For planetary bodies, what you're doing now is like trying to model the earth by simulating its position every few months and assuming it moves in a straight line in the meanwhile. You need to actually simulate its position every few minutes, not every few months.
In our example we'd probably needed our graviational direction to be based upon the average between our current position and the position at the end of this frame.
You could do that but it would be easier to simply decrease the "delta t" for the computation. Then the difference between the directions at the beginning and the end of the frame is much smaller.
Once you've got that sorted out then there are more techniques you can use. For example, you could detect when the position changes too much between frames and go back and redo the computations with a smaller time step. If the positions change hardly at all then increase the time step.
Once you've got that sorted, there are lots of more advanced techniques you can use in physics simulations, but I would start by getting basic time stepping really solid first. The more advanced techniques are essentially variations on your idea of "do a smarter interpolation of the change over the time step" -- you are on the right track here, but you should walk before you run.
I'll start with a technique that is almost as simple as the Euler-Cromer integration you've been using but is markedly more accurate. This is the leapfrog technique. The idea is very simple: position and velocity are kept at half time steps from one another.
The initial state has position and velocity at time t0. To get that half step offset, you'll need a special case for the very first step, where velocity is advanced half a time step using the acceleration at the start of the interval and then position is advanced by a full step. After this first time special case, the code works just like your Euler-Cromer integrator.
In pseudo code, the algorithm looks like
void calculate_accel (orbiting_body_collection, central_body) {
foreach (orbiting_body : orbiting_body_collection) {
delta_pos = central_body.pos - orbiting_body.pos;
orbiting_body.acc =
(central_body.mu / pow(delta_pos.magnitude(),3)) * delta_pos;
}
}
void leapfrog_step (orbiting_body_collection, central_body, delta_t) {
static bool initialized = false;
calculate_accel (orbiting_body_collection, central_body);
if (! initialized) {
initialized = true;
foreach orbiting_body {
orbiting_body.vel += orbiting_body.acc*delta_t/2.0;
orbiting_body.pos += orbiting_body.vel*delta_t;
}
}
else {
foreach orbiting_body {
orbiting_body.vel += orbiting_body.acc*delta_t;
orbiting_body.pos += orbiting_body.vel*delta_t;
}
}
}
Note that I've added acceleration as a field of each orbiting body. This was a temporary step to keep the algorithm similar to yours. Note also that I moved the calculation of acceleration to it's own separate function. That is not a temporary step. It is the first essential step to advancing to even more advanced integration techniques.
The next essential step is to undo that temporary addition of the acceleration. The accelerations properly belong to the integrator, not the body. On the other hand, the calculation of accelerations belongs to the problem space, not the integrator. You might want to add relativistic corrections, or solar radiation pressure, or planet to planet gravitational interactions. The integrator should be unaware of what goes into those accelerations are calculated. The function calculate_accels is a black box called by the integrator.
Different integrators have very different concepts of when accelerations need to be calculated. Some store a history of recent accelerations, some need an additional workspace to compute an average acceleration of some sort. Some do the same with velocities (keep a history, have some velocity workspace). Some more advanced integration techniques use a number of techniques internally, switching from one to another to provide the best balance between accuracy and CPU usage. If you want to simulate the solar system, you need an extremely accurate integrator. (And you need to move far, far away from floats. Even doubles aren't good enough for a high precision solar system integration. With floats, there's not much point going past RK4, and maybe not even leapfrog.)
Properly separating what belongs to whom (the integrator versus the problem space) makes it possible to refine the problem domain (add relativity, etc.) and makes it possible to easily switch integration techniques so you can evaluate one technique versus another.
So i found a solution, it might not be the smartest, but it works, and it's pretty came to mind after reading both Eric's answer and also reading the comment made by marcus, you could say that it's a combination of the two:
This is the new code:
foreach (ExtTerBody OtherObject in UniverseController.CurrentUniverse.ExterTerBodies.Where(x => x != this))
{
double massOther = OtherObject.Mass;
double R = Vector2Math.Distance(Position, OtherObject.Position);
double V = (massOther) / Math.Pow(R,2) * Time.DeltaTime;
float VRmod = (float)Math.Round(V/(R*0.001), 0, MidpointRounding.AwayFromZero);
if(V > R*0.01f)
{
for (int x = 0; x < VRmod; x++)
{
EulerMovement(OtherObject, Time.DeltaTime / VRmod);
}
}
else
EulerMovement(OtherObject, Time.DeltaTime);
}
public void EulerMovement(ExtTerBody OtherObject, float deltaTime)
{
double massOther = OtherObject.Mass;
double R = Vector2Math.Distance(Position, OtherObject.Position);
double V = (massOther) / Math.Pow(R, 2) * deltaTime;
Vector2 NonNormTwo = (OtherObject.Position - Position).Normalized() * V;
Vector2 NonNormDir = Velocity + NonNormTwo;
Velocity = NonNormDir;
//Debug.WriteLine("Velocity=" + Velocity);
Position += Velocity * deltaTime;
}
To explain it:
I came to the conclusion that if the problem was that the satellite had too much velocity in one frame, then why not seperate it into multiple frames? So this is what "it" does now.
When the velocity of the satellite is more than 1% of the current radius, it seperates the calculation into multiple bites, making it more precise.. This will ofcourse lower the framerate when working with high velocities, but it's okay with a project like this.
Different solutions are still very welcome. I might tweak the trigger-amounts, but the most important thing is that it works, then i can worry about making it more smooth!
Thank's everybody that took a look, and everyone who helped be find the conclusion myself! :) It's awesome that people can help like this!
Essentially there is a table and player A raises to 100$, player B calls (accepts), player C only has 50$ so the pots are created as 100$ (between player A and B) and 150$ (between all three players because everyone chips in at 50$).
How would I implement such a function and handle all the pots properly?
This is what I have so far:
static public void FillPots(Room r, decimal Val, int Player)
{
decimal NewRaise = Val;
if (NewRaise > 0)
{
foreach (Program.Player pz in r.Seated)
{
if (pz == null) { continue; }
if (pz.BuyIn < NewRaise)
{
Pot _pot = new Pot { PotSize = r.MaxPlayers, PotBuy = (NewRaise - pz.BuyIn), PotVal = new decimal[r.MaxPlayers] };
Array.Clear(_pot.PotVal, 0, r.MaxPlayers);
r.Pots.Add(_pot);
NewRaise -= (NewRaise - pz.BuyIn);
}
}
}
for (int i = 0; i < r.Pots.Count; i++)
{
if (r.Pots[i].PotVal[Player] == 0m && NewRaise >= r.Pots[i].PotBuy)
{
r.Pots[i].PotVal[Player] += r.Pots[i].PotBuy;
NewRaise -= r.Pots[i].PotBuy;
}
}
if (NewRaise > 0)
{
Pot _pot = new Pot { PotSize = r.MaxPlayers, PotBuy = (NewRaise), PotVal = new decimal[r.MaxPlayers] };
Array.Clear(_pot.PotVal, 0, r.MaxPlayers);
_pot.PotVal[Player] += NewRaise;
r.Pots.Add(_pot);
NewRaise = 0;
}
}
It's all pretty confusing. It is critical to keep the position of every individual player relative to the player number (int Player) within the array.
I can't speak to C#, but since you have no answers here I'll tell you in general how to handle poker pots. There are two methods: pot-centric and player-centric. I generally prefer the latter since there is less data to manage.
You need one variable for the "combined pot" (let's call it POT), one variable for the "current total bet amount" (CBET), one for last raise amount (LRAISE), and three variables for each player: stake (PSTAKE[N]), "current bet" (PBET[N]), "contribution to pot" (PCONTRIB[N]).
When the hand is dealt, set POT and each player's PCONTRIB[] to 0. If there were antes, add them to POT and to each player's PCONTRIB[], removing them from PSTAKE[].
At the start of each betting round, set CBET, LRAISE and all PBET[] to 0. If this is the first round, and there are blinds, set those players' PBET[] to the blind amounts, removing them from PSTAKE[].
Each player in turn has three choices: (1) fold (you might want to disallow this if CBET is 0), (2) call, in which case he must make his PBET[] equal to CBET (if CBET is 0, this is called a "check", otherwise the call amount is CBET-PBET[], which must be moved from PSTAKE[] to PBET[]). (3) raise, in which the player must increase the CBET amount by at least LRAISE (and obeying any other limits, this amount becoming the new LRAISE), moving the needed amount from his stake. Note: you also need to keep track of who the last raiser was, so that he is not allowed to raise himself.
If the player's stake is insufficient to call or raise, he can go "all in" (if otherwise allowed) moving his entire stake to his PBET[]. When all players have called CBET or gone all in, the betting round is over. If one player raised, all the others folded, and none is all in, then just award the pot to the raiser. Otherwise for each player, add his PBET[] to his PCONTRIB[] and to the POT.
If hand continues to showdown, award the pot like this: Start with the best hand: his winnable amount (W) is his PCONTRIB[]. Go to each player (including him), subtract from POT the lesser of W and that player's PCONTRIB[], and award it to winner. Remove winner from player list, remove any players whose PCONTRIB[] is now 0, and continue with next best hand. Remove that winner, etc., and continue until POT is 0.
The pot-centric method I think is more complex. Instead of keeping each player's contribution, you keep a list of central pot and side pots, and for each pot a list of players involved in that pot (there might be as many pots as players). Then the pots are awarded outside-in, instead of inside-out as above. This is more like how dealers are taught to do it in real games.
Update:
I just realized I was "skipping ahead" and not actually answering what OP was asking. But I'll leave my answer here because this question still shows up in searches related to programmatic poker pot resolution. I had already developed all the action logic prior to resolving where the chips all go.
Previous Answer:
Is it ever too late to answer an unanswered question? I was also looking for the answer to this. The answers I found were a bit confusing, so I finally wrote my own code on dotnetfiddle:
https://dotnetfiddle.net/P0wgR5
Seeing as how I didn't do it exactly as Lee Daniel Crocker mentioned, I wonder if there are bugs with what I've done. I'd be happy to fix them and update code here.
I'm not sure if it's good practice to paste 140 lines of code into StackOverflow, but that's how many lines I ended up with. The console shows what's being done, and you can experiment with hand creation in the Main function.
I tested this thoroughly, and compared the results to this pot calculator:
https://www.pokerlistings.com/rules-for-poker-all-in-situations-poker-side-pot-calculator
The results appeared to line up.