(Monogame) Update method causes extreme lag - c#

I am currently creating a 2d platformer in monogame.
I created a block that, when hit by the player, starts dissapearing. When it dissapears, i draw a rectangle around it. Every Tile of the same type (BrittleTile) also starts dissapearing, untill the entire connected mob of BrittleTiles has dissapeared.
The problem is that for every BrittleTile that is destroyed, my game runs noticably slower, until it becomes a slideshow after 10 or so BrittleTiles destroyed. I have no idea as to what may cause this, i've been trying to ease the Update method of the class but nothing seems to help.
Any idea as to what may cause this?
class BrittleTile: Tile, IIncludeSound
{
public Rectangle DestroyedCheckRectangle;
private BrittleTile brittle;
private bool _isPlayed;
private bool _hasBroken;
private SoundEffect _sfxBreak;
private SoundEffectInstance _sfxiBreak;
private TimeSpan _breakTimer = new TimeSpan();
public Rectangle BrokenViewRectangle { get; set; }
private bool _isBreaking = false;
public BrittleTile(Texture2D texture, Rectangle baseViewRectangle, Rectangle brokenViewRectangle):base(texture, baseViewRectangle, true )
{
this.BrokenViewRectangle = brokenViewRectangle;
this.ViewRectangle = baseViewRectangle;
}
public void Update(GameTime gameTime, Hero hero, Entity[,] grid)
{
if (!this._hasBroken)
{
if (!this._isBreaking && !this._hasBroken && this.hasCollision)
_checkCollision(hero, grid);
if (this._isBreaking)
{
if (!this._isPlayed)
_sfxiBreak.Play();
this._isPlayed = true;
this._breakTimer += gameTime.ElapsedGameTime;
if (this._breakTimer.TotalMilliseconds < 250)
this.GhostMode(gameTime);
else
{
this._isBreaking = false;
this.hasCollision = false;
this._breakTimer -= this._breakTimer;
}
}
else
{
if (!this.hasCollision && this.DestroyedCheckRectangle.Width == 0)
{
this.DestroyedCheckRectangle.X = this.DestinationRectangle.X - 10;
this.DestroyedCheckRectangle.Y = this.DestinationRectangle.Y - 10;
this.DestroyedCheckRectangle.Height = this.DestinationRectangle.Height + 20;
this.DestroyedCheckRectangle.Width = this.DestinationRectangle.Width + 20;
this._hasBroken = true;
}
}
}
}
public override void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(this.Texture, this.DestinationRectangle, this.BrokenViewRectangle, Color.White);
if (this.hasCollision)
spriteBatch.Draw(this.Texture, this.DestinationRectangle, this.ViewRectangle, Color.White * this.GhostDraw * 0.9f);
spriteBatch.Draw(this.Texture, DestroyedCheckRectangle, new Rectangle(1, 1, 1, 1), Color.Yellow);
Console.WriteLine(this.DestroyedCheckRectangle.X + ", " + this.DestroyedCheckRectangle.Y + ", " + this.DestroyedCheckRectangle.Width + ", " + this.DestroyedCheckRectangle.Height);
}
private void _checkCollision(Hero hero, Entity[,] grid)
{
if (this.DestinationRectangle.Intersects(hero.AttackHitBox))
{
this._isBreaking = true;
}
foreach (Shuriken star in hero.Stars)
{
if (this.DestinationRectangle.Intersects(star.DestinationRectangle))
this._isBreaking = true;
}
foreach (Entity entityObject in grid)
{
if (hasCollision && entityObject.GetType() == typeof(BrittleTile)){
brittle = entityObject.DeepCopy() as BrittleTile;
if (this.DestinationRectangle.Intersects(brittle.DestroyedCheckRectangle))
this._isBreaking = true;
}
}
}
override public void LoadSounds(ContentManager content)
{
this._sfxBreak = content.Load<SoundEffect>("SFX/brittleBreak");
_sfxiBreak = _sfxBreak.CreateInstance();
_sfxiBreak.Volume = 0.2f;
}
}

First, this line:
if (!this._isBreaking && !this._hasBroken && this.hasCollision)
the !this._hasBroken is superfluous.
My first warning sign is this line:
brittle = entityObject.DeepCopy() as BrittleTile;
I assume DeepCopy() makes a new version of the object, and copies all it's properties, right? Seeing the code of that might help pin it down, but on that assumption...
For every Tile, you're cycling through every object in you're grid and you're completely duplicating that object as a BrittleTile, why?
My first change would be to modify that foreach to have this within it:
var brittle = entityObject as BrittleTile
if (brittle != null && hasCollision){
if (this.DestinationRectangle.Intersects(brittle.DestroyedCheckRectangle))
this._isBreaking = true;
}
}
Not sure this is the primary cause of the slowdown, but it is definitely an issue. If you're cloning an object and then immediately throwing an object away, you're almost certainly doing something wrong. If you're cloning an object and you're not editing the properties of that copy at all (and using those edits), you're definitely doing something wrong. I'd be wary before using a method like that unless you're really sure that's what you want.

Related

Unity Instantiated gameobject does not run function unless it is the most recently Instantiated object. Why might that be happening and how to fix it?

So, I have a series of gameobject instances all with a script on them. When i select one of these instances a black circle appears AND a UI element should be instantiated with some stats.
It works if there is only one instance of the gameobject without any issues.
When more object are added and selection changed from one to the other, the circle works perfectly fine, but the UI instantiate only works for the most recently instantiated of the objects.
Here is the code:https://hatebin.com/oihkmmbmeq
This is the function on the Tower(the object that has more instances):
private void Selected_Change()
{
if (IsSelected)
{
line.enabled = true;
GameObject.FindGameObjectWithTag("UI").GetComponent<UI>().Spawn_Tower_Panel(this.gameObject, 150, "Radius: " + XRad + "\nValue: " + 150 + "\nRotSpeed: " + 7f + "\nDamage: " + Damage + "\n UpPrice" + UpPrice);
}
else
{
line.enabled = false;
GameObject.FindGameObjectWithTag("UI").GetComponent<UI>().Despawn_Tower_Panel();
}
}
And this is the code in the UI script:
public void Spawn_Tower_Panel(GameObject tower,int Value, string Stats)
{
Tower_Panel_Inst = Instantiate(Tower_Panel, Tower_Panel.transform.position, Tower_Panel.transform.rotation);
Tower_Panel_Inst.transform.SetParent(this.transform, false);
Selected_Object = tower;
Tower_Panel_Inst.transform.Find("TowerTypeBackground").transform.Find("TowerType").GetComponent<Text>().text = tower.name.Remove(tower.name.Length - 7);
Tower_Panel_Inst.transform.Find("Values_Stuff").GetComponent<Text>().text = Stats;
TempInt = Value;
}
public void Despawn_Tower_Panel()
{
Destroy(Tower_Panel_Inst);
}
public void Scrap_Tower()
{
TempInt = Mathf.RoundToInt(TempInt * 0.5f);
CreditsChange(TempInt);
Destroy(Tower_Panel_Inst);
Destroy(Selected_Object);
TempInt = 0;
}
public void Upgrade_Tower()
{
if(Selected_Object.name == "Tower_Cannon(Clone)")
{
int temp = Selected_Object.GetComponent<Tower_Cannon>().Upgrade_Price();
if(Materials - temp > 0)
{
CreditsChange(-temp);
Selected_Object.GetComponent<Tower_Cannon>().Upgrade();
}
Destroy(Tower_Panel_Inst);
}
}
https://youtu.be/JN1IgFd_qyU This is a video that shows what happens, and what the problem is.
Thank you for taking the time to look into this

Tiles not following input in a branching board game path

I'm making a board game in Unity with multiple branching paths where the players can decide which one to follow. (Think the Mario Part game board) But the player input on which path to follow isn't working.
So I have a script that has the tokens follow a set path and (Linked List for the path) with a for loop to have the tokens move their allocated amount. Within the loop, I have an If statement that checks how many paths the tile has and if there are more than one if runs a separate script that pauses the game and offers a menu prompt for the players to pick one path or the other.
I've tried tossing in a few contitions for continuing the game within the loop but they either ignore the player input, are an input behind or just break the code.
`for (int i = 0; i < spacesToMove; i++)
{
if (final_Waypoint == null)
{
final_Waypoint = Starting_Waypoint;
}
else
{
if (final_Waypoint.Next_Waypoint == null || final_Waypoint.Next_Waypoint.Length == 0)
{
//we are overshooting the final waypoint, so just return some nulls in the array
//just break and we'll return the array, which is going to have nulls at the end.
Debug.Log(Current_Waypoint);
break;
}
else if (final_Waypoint.Next_Waypoint.Length > 1) // && if playerID == 0 so it only appears for players.
{
menu.Pause_for_Choice();
//if (menu.ButtonPress == true)
final_Waypoint = final_Waypoint.Next_Waypoint[menu.choice_int];
//***There's a bug here. It calls menu as per normal, and the game pauses.
//But when I click 'no', choice_int isn't updated first: this function finishes first,
//so this function gets old choice_int. THEN the button function executes.
Debug.Log("test 3 " + menu.choice_int);
}
else
{
final_Waypoint = final_Waypoint.Next_Waypoint[0];
}
}
listOfWaypoints[i] = final_Waypoint;
Debug.Log("i:" + i);
}
return listOfWaypoints;
}`
`{
public GameObject choice_Menu;
public bool isPaused = true;
public int choice_int=0;
public int choice_placeholder = 0;
public void Pause_for_Choice()
{
isPaused = true;
choice_Menu.SetActive(true);
Time.timeScale = 0;
}
public void Yes()
{
choice_placeholder = 0;
UnPause_Game();
}
public void No()
{
choice_placeholder = 1;
UnPause_Game();
}
public void UnPause_Game()
{
isPaused = false;
choice_Menu.SetActive(false);
Time.timeScale = 1;
}
}`

Game objects scaling up when transform.parent set to null

I'm working on a small game where objects are put in a boat, then a key press makes the boat "sail".
To move all the objects that are standing on the boat, i am setting the parent of each object to an empty guide object in the boat then changing the position of the boat. (I have also tried parenting the objects into the boat object itself)
The following is a script applied to the boat object.
variables set in the BoatScript class:
public class BoatScript : MonoBehaviour {
public List<string> boatList;
public KeyCode interact;
public GameObject tempObject;
public string whichSide;
public string direction;
public bool canSail;
}
Start and Update method:
void Start () {
canSail = false;
whichSide = "start";
direction = "toFinish";
speed = 0f;
}
void Update () {
if (canSail == true)
{
SetSail();
}
if (boatList.Contains("FARMER") && whichSide == "start" && Input.GetKeyDown(interact))
{
speed = 0.5f;
CharacterCheck();
}
else if (boatList.Contains("FARMER") && whichSide == "finish" && Input.GetKeyDown(interact))
{
speed = -0.05f;
CharacterCheck();
}
}
Here are my OnTrigger methods:
void OnTriggerEnter(Collider other)
{
Debug.Log(other.gameObject.name + " collided with " + gameObject.name);
promptText.text = "";
if(CheckValidObject(other.gameObject.name) == true) {
boatList.Add(other.gameObject.name);
logBox.text = logBox.text + "\nThe " + other.gameObject.name + " is in the boat";
}
if (other.gameObject.name == "FARMER")
{
promptText2.text = "Press E to set sail";
}
}
void OnTriggerExit(Collider other)
{
boatList.Remove(other.gameObject.name);
logBox.text = logBox.text + "\nThe " + other.gameObject.name + " has left the boat";
promptText.text = "";
if (other.gameObject.name == "FARMER")
{
promptText2.text = "";
}
}
Setting sail:
void SetSail()
{
promptText.text = "";
promptText2.text = "";
addParents();
if (whichSide == "sailing" && direction == "toFinish")
{
speed = 0.05f;
gameObject.transform.Translate(speed, 0, 0);
}
else if (whichSide == "sailing" && direction == "toStart")
{
speed = -0.05f;
gameObject.transform.Translate(speed, 0, 0);
}
else if (whichSide == "start" || whichSide == "finish")
{
gameObject.transform.Translate(speed, 0, 0);
removeParents();
}
}
void addParents()
{
foreach(string o in boatList)
{
GameObject obj = GameObject.Find(o);
obj.GetComponent<Rigidbody>().useGravity = false;
obj.GetComponent<Rigidbody>().isKinematic = true;
if (obj.name == "FARMER") { obj.transform.parent = playerGuide.transform; }
else {obj.transform.parent = itemGuide.transform; }
}
}
void removeParents()
{
foreach (string o in boatList)
{
GameObject obj = GameObject.Find(o);
obj.GetComponent<Rigidbody>().useGravity = true;
if(obj.name != "FARMER") {obj.GetComponent<Rigidbody>().isKinematic = false; }
obj.transform.parent = null;
}
}
The problem: Once the boat reaches and hits the collider for the other side, the boat stops as expected but the objects that were just removed from the parent begin to scale up continuously like this:
e.g 1 https://i.gyazo.com/d35ae729757b8e71c25fd1b4a3857dae.mp4
e.g 2 https://i.gyazo.com/80637919bfd114a42d187300b7faef25.mp4
I'm not too sure what is causing this. Any help is much appreciated, thank you.
Instead of setting the parent via transform.parent, use transform.SetParent(targetTransform, false);. The second, bool, parameter determines if the game object's transform will maintain it's position, orientation, and scale. By setting it to false, the transform will maintain it's current values, while setting it to true will modify the position, orientation, and scale to maintain the world position. You can check this for further info transform.SetParent
Are youu sure it is scaling up and not moving up in the Z axes? From what im looking it is going towards the camera but not scaling up. You should debug the position and scale in the update method to see whats really happening there.
Below comment: "Well then you will have to debug it more carefully, i would first try, setting canSail to false as soon as it reaches the end. Perhaps the method addParent which is always being executed is wrong, what does the object itemGuide does? edit: i just seen the second video, from my perspective that seems gravity, what you meant with problems with scaling up is because its moving out from the boat?"
Solution:
void SetSail()
{
promptText.text = "";
promptText2.text = "";
addParents();
if (whichSide == "sailing" && direction == "toFinish")
{
speed = 0.05f;
gameObject.transform.Translate(speed, 0, 0);
}
else if (whichSide == "sailing" && direction == "toStart")
{
speed = -0.05f;
gameObject.transform.Translate(speed, 0, 0);
}
else if (whichSide == "start" || whichSide == "finish")
{
gameObject.transform.Translate(speed, 0, 0);
canSail = false; //Adding this line solves the issues, must have been affecting transform properties to child objects.
removeParents();
}
}

C# RPG - Ability Cooldown Issues

I am working on an RPG and started working on ability cooldowns. I have a player that is controlled by the user (player1), and a player controlled by the computer (player2) for testing purposes. The cooldown for abilities work perfectly for the user controlled player, but when it comes to the AI, the computer will not execute the ability unless the computer goes first (when the ability is off cooldown by default). Below is the code I think is relevant to the question, but if more information is needed I will update the post.
The ability class:
using UnityEngine;
public class attackList : MonoBehaviour{
public int dmg;
public float coolDown;
public float _attackTimer;
public void Attack1(basePlayer user, basePlayer target, int D)
{
coolDown = 2.5f;
if (user._attackTimer == 0)
{
dmg = D;
user._attackTimer = coolDown;
target.curHealth -= dmg;
}
}
public void cooldown(basePlayer user)
{
if (user._attackTimer > 0) {
user._attackTimer -= Time.deltaTime;
if (user._attackTimer < 0) {
user._attackTimer = 0;
}
}
}
}
The attack logic class:
public class attackLogic : MonoBehaviour {
private attackList _attackList = new attackList();
private playerList _playerList = new playerList();
public bool player1_turn = false; //player1 is not allowed to go first by default
public bool player2_turn = false; //player2 is not allowed to go first by default
void Start()
{
int rnd = Random.Range (1,3); // random value between 1 and 2 generated, which will determine what player goes first
if (rnd == 1)
{
player1_turn = true;
}
if (rnd == 2)
{
player2_turn = true;
}
//current health set to the maximum health at the beginning of each fight
_playerList.player1.curHealth = _playerList.player1.maxHealth;
_playerList.player2.curHealth = _playerList.player2.maxHealth;
}
void Update()
{
Logic(); //run through the logic of the battle
}
public void Logic()
{
if (player1_turn == true)
{
_attackList.cooldown(_playerList.player1);
if (Input.GetKeyUp("1"))
{
_attackList.Attack1(_playerList.player1, _playerList.player2, 5);
player1_turn = false;
player2_turn = true;
}
}
if (player2_turn == true)
{
_attackList.cooldown(_playerList.player2);
_attackList.Attack1(_playerList.player2, _playerList.player1, 5);
player1_turn = true;
player2_turn = false;
}
}
}
1) I have a nasty suspicion that the actual problem is in Time.deltaTime, but you didn't show the code so I can't confirm this.
2) Since you have time-based code here you can't expect it to behave the same when you step through the code. Thus it often helps to dust off some of the old techniques--namely, print statements. Of course things have changed enough over the years that it's become write but the ideas still work. Take a part of the screen that isn't important to what you're testing and write the variables out that are involved in what you are doing. I think you'll find player 2 is cooling down extremely slowly.
3) I think you need a better understanding of objects. You appear to be simply using them as containers. I also dislike player1 and player2--you should simply have a list of players. Remember how the WOPR was defeated in War Games?--that can be very useful at times in balancing things.

Making a button Xna / MonoGame

I am wondering what the best way to make a button in MonoGame is. I tried to look for an answer but none seem to be out there. Is it bad practice to use WinForm buttons? If so, what viable alternatives exist?
I always like making a custom button class, this gives me a lot of flexibility for creating creative buttons.
The class creates a button with a texture and a position x and position y and a unique name, after i've done that i'll check my mouse position and see if it's inside the button or not, if it is inside the button then it can click the button and it will search the button by name and execute the given command :)
Here is an example of my button class: (not the best way, but hey it works perfectly for me)
public class Button : GameObject
{
int buttonX, buttonY;
public int ButtonX
{
get
{
return buttonX;
}
}
public int ButtonY
{
get
{
return buttonY;
}
}
public Button(string name, Texture2D texture, int buttonX, int buttonY)
{
this.Name = name;
this.Texture = texture;
this.buttonX = buttonX;
this.buttonY = buttonY;
}
/**
* #return true: If a player enters the button with mouse
*/
public bool enterButton()
{
if (MouseInput.getMouseX() < buttonX + Texture.Width &&
MouseInput.getMouseX() > buttonX &&
MouseInput.getMouseY() < buttonY + Texture.Height &&
MouseInput.getMouseY() > buttonY)
{
return true;
}
return false;
}
public void Update(GameTime gameTime)
{
if (enterButton() && MouseInput.LastMouseState.LeftButton == ButtonState.Released && MouseInput.MouseState.LeftButton == ButtonState.Pressed)
{
switch (Name)
{
case "buy_normal_fish": //the name of the button
if (Player.Gold >= 10)
{
ScreenManager.addFriendly("normal_fish", new Vector2(100, 100), 100, -3, 10, 100);
Player.Gold -= 10;
}
break;
default:
break;
}
}
}
public void Draw()
{
Screens.ScreenManager.Sprites.Draw(Texture, new Rectangle((int)ButtonX, (int)ButtonY, Texture.Width, Texture.Height), Color.White);
}
}

Categories