Game loop collision detection performance - c#

I'm working on a Windows 10 2D board game, that sometimes can generate a chain reaction, where 144 moving objects can collide with 36 non-moving objects. This is the "worst" possible situation.
Every moving game object is created by some non-moving game object. The non-moving game object "shoots" moving game objects, and something happens when they collide with other non-moving game object :)
I'm using storyboards for moving XAML objects (UserControls), and checking for collisions with game loop based on CompositionTarget.Rendering.
Everything works great at my PC, but problem occures when I'm trying to make this effect on phone.
My game loop looks like this:
foreach (var movingGameObject in GameCanvas.Children.OfType<MovingGameObect>())
{
if (movingGameObject.Visibility == Visibility.Visible)
CheckCollision(movingGameObject);
}
CheckCollision method looks like this:
var left = Canvas.GetLeft(movingGameObject);
var top = Canvas.GetTop(movingGameObject);
var right = left + movingGameObject.Width;
var bottom = top + movingGameObject.Height;
// If left Canvas - remove
if (left <= 0 || right >= GameCanvas.ActualWidth || top <= 0 || bottom >= GameCanvas.ActualHeight)
{
RemoveMovingGameObject(movingGameObject);
return;
}
else
{
// Since it can go 4 ways - I check if it moves up, down, right or left
if (movingGameObject.Way == Way.Left)
{
for (/* this is being done max 5 times, I checked it. It gets every possible game object id that may be on the way of moving game object */)
{
if (this._gameObjects[i].IntersectsWith(movingGameObject))
{
RemoveMovingGameObject(movingGameObject);
GameObjectHit(this._gameObjects[i], null);
return;
}
}
}
else if (movingGameObject.Way == Way.Right)
{
//SAME HERE
}
// SAME FOR UP and DOWN
}
The GameObjectHit method checks if non-moving game object has to shoot another moving game objects etc. So when this is very bad scenario, game loop iteration has to check for collision and sometimes produce another moving object(s) and shoot it(them).
And IntersectsWith method:
var x1 = Canvas.GetLeft(this);
var y1 = Canvas.GetTop(this);
var r1 = new Rect(x1, y1, this.ActualWidth, this.ActualHeight);
var x2 = Canvas.GetLeft(mgo2);
var y2 = Canvas.GetTop(mgo2);
var r2 = new Rect(x2, y2, mgo2.ActualWidth, mgo2.ActualHeight);
r1.Intersect(r2);
if (!r1.IsEmpty)
return true;
else
return false;`
Oh, almost forgot. What's happening on phone? Game loop is being called not enough times and it don't check for collisions fast enough (moving objects just fly through non-moving objects).
I tried everything I possibly can to tweek a performance. I know that Windows 10 Mobile is in beta version and it will be faster, but the gameloop is being called ~2 times a second when it gets hot, so that's not the system fault.

For anybody struggling with this problem in the future:
YES - this was a XAML-based performance issue.
I re-wrote the code with MonoGame (XNA) and there is no problem. Everything works just fine even on low-budget Nokia Lumia 635:)
I did not try Win2D option, but suspect the result would be as good as it is with MonoGame.
Thanks everybody for replies.

Related

Unity: Dial-like Animation Resetting

(For anyone that understands, this is a simulator for an Omnitrix from Ben10 lol)
Explanation: I am trying to create a dial-like program. When an arrow key is pressed, an animation for a dial rotating plays, but it resets to the default position right after. The animation rotates the dial 20 degrees to the right or left, depending on the key pressed. How do I make the object stay at the 20-degree angle after the animation has played, and have the next animation start at the 20-degree angle and rotate to 40 degrees?
What I tried was to have the animation play and then change the rotation angle of the object to 20 degrees directly after so that it could increase to 40 from there, but that did not work (for reasons I have not figured out).
Code:
Ignore the inner if and print statements; those work as intended for debugging other areas of the project. The transform.Rotate statement at the end is the way that I tried to have my idea work, but it was unfunctional.
void Update()
{
if (Input.GetKeyDown("d") || Input.GetKeyDown("right"))
{
anim.Play("twistRight");
if (numAliens >= 17)
{
numAliens = 0;
}
else
{
numAliens++;
}
print("right" + numAliens);
twistObject.transform.Rotate(0, 20, 0);
}
if (Input.GetKeyDown("a") || Input.GetKeyDown("left"))
{
anim.Play("twistLeft");
if (numAliens <= 0)
{
numAliens = 17;
}
else
{
numAliens--;
}
print("left" + numAliens);
twistObject.transform.Rotate(0, -20, 0);
}
}
Animator: I think that the fact that the animations transition into the default state is what is causing this, but I do not know how else to have this program work under my current (unfunctional) structure of code. Without transitioning into the default state, it does not let me play the same animation multiple times consecutively, so I felt that this was the only option.
Any and all help is greatly appreciated. Thank you in advance!
Link to demonstration: https://drive.google.com/file/d/1I5Z2lQ-R9nWzC2rQ7Z389YGldmQcaeU1/view?usp=sharing

Collisions in custom physics problems

I'm kind of new to Unity. As by the title, I am having trouble getting the collisions right in my game. I am using the custom physics script from unity: https://unity3d.com/learn/tutorials/topics/2d-game-creation/scripting-gravity?playlist=17093. In my game, I am experiencing difficulties in disable collisions.
For example, when I use
Physics2D.IgnoreLayerCollision (8, 9);
It doesn't change anything and the two characters still collide. Also, for some reasons, the triggers behave strange and are still affected by collisions. Going near a character with triggers will make him float up. I've been stuck on this for a long time and I'd really appreciate the help.
It is worth noting that I am using custom physics and using a box collider 2d to detect attack ranges etc. Here is some code of that:
Collider2D attackbox = Physics2D.OverlapBox (attackPos.position, new Vector2 (attackRangeX, attackRangeY), 0, whatIsPlayer);
if (attackbox != null && !isDead && characterController.isDead == false) {
targetVelocity = Vector2.zero;
animator.SetTrigger ("isPunching");
timeBtwAtk = startTimeBtwAtk;
}
and I have individual punch triggers in the animation to detect when the character is actually being hit:
public void SetColliderIndex(int spriteNum)
{
colliders[currentColliderIndex].enabled = false;
currentColliderIndex = spriteNum;
colliders[currentColliderIndex].enabled = true;
}
public void ClearColliderIndex()
{
colliders[currentColliderIndex].enabled = false;
}
void OnTriggerEnter2D(Collider2D col)
{
if (col.tag == "Player")
{
col.GetComponent<CharacterController2D> ().TakeDamage (enemyDamage);
col.GetComponent<CharacterController2D> ().canWait = false;
col.GetComponent<CharacterController2D> ().canControl = false;
}
}
When I use Physics2D.IgnoreLayerCollision (8, 9); I want both specified layers not to interact whatsoever. I don't want any weird shifts, or floating when they pass through each other. Also when I move my player agains the enemy, I don't want him to be able to push him back. I want it to be as if he is running into a wall.
I have answered something similar to this here. Also, if you don't want the player suddenly floating upon collision, you can just set the rigidbody/rigidbody2d type to kinematic, but this mean the object will not be affected by physics. you will have to do that in code.
i think your problem is that at some point the two colliders interact and/or one of the two gets activated back, but from the code that you posted i can't clearly identify the problem if there's any

Moving an enemy back and forth in Monogame / XNA

I am trying to achieve a very basic 2D game right now where an enemy is on the screen and it bounces back and forth between set points (50 and 500) kind of like a space invaders sort of thing. My issue is I can only get it to go right, but then not come back towards the left and repeat.
I was messing around with coding it myself before bothering to look into it and actually figure it out but I thought I had something that would work, but well it doesn't, my issue is I don' get why.
My code is supposed to work like a switch, two if statements within the Update loop, one comes on the other goes off, one moves it right the other moves it left, I thought that was fine but it doesn't do that. It moves it right just fine but then the left part just doesn't work.
So, why does the following code not work?
namespace _2D_game_num1
{
class Enemy
{
int health;
Vector2 enemy_location = new Vector2(50, 50);
Vector2 enemy_speed = new Vector2(1, 1);
Player player = new Player("dummy");
public Enemy()
{
health = 100;
}
public void UpdateLocation()
{
//Vector2 player_pos = player.GetLocation();
//if (player_pos.X < 200)
// Using the players location to figure out where the enemy should move
bool right = true;
bool left = false;
if (right)
{
enemy_location.X += enemy_speed.X;
if (enemy_location.X == 500)
{
right = false;
left = true;
}
}
if (left)
{
enemy_location.X -= enemy_speed.X;
if (enemy_location.X == 50)
{
right = true;
left = false;
}
}
}
public Vector2 GetLocation()
{
return enemy_location;
}
}
}
And then in the main Game class I have it so enemy1.UpdateLocation(); is within the Update section correctly (along with my players movement which works fine).
Try this:
public void UpdateLocation()
{
enemy_location.X += enemy_speed.X;
if (enemy_location.X <= 50 || enemy_location.X >= 500)
{
enemy_speed.X = new Vector2(-enemy_speed.X, enemy_speed.Y);
}
}
What we are doing here is moving the enemy based on it's current speed. Then if the location is on the left or right of the screen, change direction.
Sometimes it pays to keep things simple. Get rid of the left and right flags, as they are just confusing things. When you're dealing with Update methods you're typically changing the state of something. The way you had it before, the left and right state was getting reset every time UpdateLocation is called.
Btw, you might want to consider passing in GameTime to your Update methods. Typically, when you're moving things around in real-time you'll want to multiply movement by some kind of deltaTime to keep things smooth on all devices. You're probably getting away with it because by default it'll be a fixed frame rate, but that may not always be the case, so it's a good habit to get into.

Farseer Assertion Failure

We're creating a game for a school project. It's a 2D platformer and it is in its very early stages. We use C#/XNA and we're implementing Farseer Physics Engine.
I'm currently struggling with the map-class. In the class we have a List of DrawableGameObjects, were we store each tile of the map and draw them. But when we try to draw them we get a "Assertion Failed". Examining the problem even further I've come to the conclusion that whenever we try to add more than to static bodies to the world (even without drawing them) we get this failure. Throw message
Game1.cs:line 210 is:
world.Step(0.033333f);
And Program.cs:line 15 is:
game.Run();
Here is the code for the Map class:
class Map
{
private List<DrawableGameObject> ground = new List<DrawableGameObject>();
public Map(World world, Texture2D texture)
{
for (int i = 0; i < 32; i++)
{
DrawableGameObject floor = new DrawableGameObject(world, texture, new Vector2(40, 40), 100, "ground");
floor.Position = new Vector2(i * 40, 500);
floor.body.BodyType = BodyType.Static;
ground.Add(floor);
}
}
public void Draw(SpriteBatch spriteBatch){
foreach (DrawableGameObject dgo in ground)
dgo.Draw(spriteBatch);
}
}
Any ideas? I've posted the problem on Farseer's forum, but they haven't been very helpful yet...
This is a bug in Farseer. (Version 3.3.1)
I opened up the Farseer source code to the method in question (World.SolveTOI) and found two calls to Debug.Assert. And, in fact, in my copy of the code, I've actually already come across this bug and commented one of them out, specifically:
Debug.Assert(typeA == BodyType.Dynamic || typeB == BodyType.Dynamic);
Basically it doesn't want to attempt to handle contacts between two bodies that are static.
Fortunately the code immediately below actually checks for essentially the same condition, and continues the loop if that is the case:
bool awakeA = bA.Awake && typeA != BodyType.Static;
bool awakeB = bB.Awake && typeB != BodyType.Static;
// Is at least one body awake?
if (awakeA == false && awakeB == false)
{
continue;
}
So it's quite safe to simply comment out or remove the assertion. (You should, of course, be building Farseer from source - it makes life much easier.)
To reproduce the Farseer bug: Have two static bodies and one dynamic body that is in contact with both, then make the dynamic body static. The assert will trigger.
The assert is in the contact handling loop. Normally a pair of static bodies wouldn't create contacts. But if a body starts out as dynamic, contacts can be created - they don't get removed when the body is made static.

Testing For All Non-collided Tiles in XNA

I'm currently writing a platforming game in XNA.
My collision testing currently operates by testing the bounding box of the player with every tile's bounding box by running through a foreach loop. However, I can't figure out how to test whether the player is NOT touching any blocks.
How would I run through my array and test for if they player isn't touching any blocks so that I can check if he is in mid air? Any help or advice would be greatly appreciated. Thank you in advance!
/////////////////////////////////////
My collision code is
if (personRectangle.TouchTopOf(newRectangle))
{
onGround = true;
test = false;
test2 = true;
}
The corresponding bool test is
public static bool TouchTopOf(this Rectangle r1, Rectangle r2)
{
return (r1.Bottom >= r2.Top - 1 &&
r1.Bottom <= r2.Top + (r2.Height / 2) &&
r1.Right >= r2.Left + (r2.Width / 5) &&
r1.Left <= r2.Right - (r2.Width / 5));
}
And this piece of code tests collision in the Game1.cs update.
foreach (CollisionTiles tile in map.CollisionTiles)
player.Collision(tile.Rectangle);
Which is a list of tiles in my map class
private List<CollisionTiles> collisionTiles = new List<CollisionTiles>();
XNA Rectangles have a built-in intersects method:
if (rect1.Intersects(rect2)){
...
}
just loop through the tile rectangles and call intersects on each one. To make it more efficient you should map the player coords onto world coords and determine the tiles nearest to the player, doing this you can narrow down the number of tiles you need to loop over.
EDIT: for clarification, you could have:
bool collision=false;
foreach (CollisionTiles tile in map.CollisionTiles) {
if(personRectangle.Intersects(tile.Rectangle)) {
collision =true;
break;
}
}
Then if collision is true you know there was a collision somewhere.
Unless you have a reason not to, keep it simple. Just keep track of how many things you collided with. If your count ends up being 0 then you didn't collide with anything.
Without worrying about modifying / optimizing any of the code you have, it could be something like:
int touchedCount = 0;
foreach (CollisionTiles tile in map.CollisionTiles)
{
if (player.Collision(tile.Rectangle))
{
touchedCount++;
}
}
if (touchedCount == 0)
{
//You did not collide with anything
}
Then inside your collision method do something similar to keep track of your checks and if you collided at all. This would probably be easiest using Rectangle.Intersects as the other answer suggests.

Categories