- Home /
The game is running slow for no reason
Hi, I'm making a sort of tetris game that i want to run on WP8.1.
The game is almost finished but when it comes to play, at the beginning the game is smooth but the more there are blocks added the more is slows down. It's not supposed to happen since everytime you place a block its script is disabled (and so update is not called anymore) so why would it slow the game?
I saw on other questions that some people had problems with 3d game using like 3000 objects a bit slow on a device cheaper than mine while my game is 2d and only uses about 20/30 objects max.
Any clue to what i could check ?
Thanks.
PS : I must warn you, I'm under unity 4. I tried to port it to unity 5 but I couldn't and hopefully I made a backup before because I would have lost my project entirely :(
Sounds like something to do with lights. Do you have lights in your scene ? What is the draw call number ? It should not exceed 30 if youre going for low end devices.
Do you have a LOT of warnings or errors on runtime? I've found that unity 5 unlike unity 4 has problems slowing down if I have warnings or errors at runtime like 'null reference' errors on my scripts. I'm working on a multiplayer game at the moment and I have a GUI error and it dupliates warnings that slow my game down to about 0.8 fps aha. Check those,
I'm not using any light, and no physics on the blocks (there is no gravity, unlike in Tetris, so I move the pieces manually if I have to). No errors, no warnings !
Answer by Deadcow_ · Apr 07, 2015 at 08:21 AM
Yep, it will be imposible to determine the problem without sources. The best is to give us full project, if possible.
If not, try to beat the problem on your own. Try to use profiler with windows phone to figure which part of gameplay is causing the problem. The profiler is free now since Unity 5 released. It'll be helpful to use Profiler.BeginSample and Profiler.EndSample to check out pieces of code that may hit performance. Good luck
By the way, I had tested my 2d game on my old HTC 8s and it run at max fps while doing a lot :) There were a dozens of sprites dinamically generated (a few in a sec), removed (I use object pool for this), moved over time really fast etc. So I beleive there is a tiny problem in your game and it'll not be to hard to find out :)
But all that can do is find a reason it runs slowly. In the tittle, the OP clearly says it happens for no reason.
(seriously, "for no reason" is always code for "I'm too frustrated to continue right now, and am possible bla$$anonymous$$g the hardware before I just quit." Step 1 when you hear yourself say that is to stop and get some rest.)
@Deadcow_ Thank you, i'm going to try profiler as soon as i can !
@Owen Reynolds "Any clue to what i could check ?" ---> I'm searching for helpers, not haters. If you want to hate check "I'm a hater" on Google, there's plenty of things to hate. I'm, of course, not bla$$anonymous$$g the hardware, but myself. Still I really want to make my game work properly, so please let me find the tips given by the community.
Answer by Gridix · Apr 07, 2015 at 01:16 PM
Here's the main class :
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class Grid : MonoBehaviour
{
public static int score = 0;
public static string message = "";
public static string recordText = "RECORD\n<size=20>"+PlayerPrefs.GetInt("Player Best Score")+"</size>";
public static bool stocked = false;
public static Group stockedGroup = null;
public static Group actualGroup = null;
public static Group cancelActualGroup = null;
public static Group cancelRandomGroup = null;
public static int cancelScore = 0;
public static int w = 10;
public static int h = 10;
public static bool quitCheck = false;
public static bool muted = false;
public static List<int> deletedRows;
public static List<int> deletedColumns;
public static Transform[,] grid = new Transform[w, h];
public static Transform[,] deleted = new Transform[w, h];
public static Spawner spawner;
void Start()
{
quitCheck = false;
}
public static Vector2 roundVec2(Vector2 v)
{
return new Vector2(Mathf.Round(v.x),
Mathf.Round(v.y));
}
public static Vector3 roundVec3(Vector3 v)
{
return new Vector3(Mathf.Round(v.x),
Mathf.Round(v.y),
v.z);
}
public static bool insideBorder(Vector2 pos)
{
return ((int)pos.x >= 0 &&
(int)pos.x < w &&
(int)pos.y >= 0 &&
(int)pos.y < h);
}
public static void deleteRow(int y)
{
for (int x = 0; x < w; x++)
{
grid[x, y].renderer.enabled = false;
//grid[x, y].position += new Vector3(-10F,0F,0F);
deleted[x, y] = grid[x, y];
grid[x, y] = null;
}
}
public static void deleteColumn(int x)
{
for (int y = 0; y < h; y++)
{
if (grid[x, y] != null)
{
grid[x, y].renderer.enabled = false;
//grid[x, y].position += new Vector3(-10F, 0F, 0F);
deleted[x, y] = grid[x, y];
grid[x, y] = null;
}
}
}
public static void decreaseRow(int y)
{
for (int x = 0; x < w; x++)
{
grid[x, y - 1] = grid[x, y]; // Move one towards bottom
if (grid[x, y - 1] != null) { grid[x, y - 1].position += new Vector3(0, -1, 0); } // Update Block position
grid[x, y] = null;
}
}
public static void increaseRow(int y)
{
for (int x = 0; x < w; x++)
{
grid[x, y + 1] = grid[x, y]; // Move one towards top
if (grid[x, y + 1] != null) { grid[x, y + 1].position += new Vector3(0, 1, 0); } // Update Block position
grid[x, y] = null;
}
}
public static void decreaseColumn(int x)
{
for (int y = 0; y < h; y++)
{
grid[x - 1, y] = grid[x, y]; // Move one towards left
if (grid[x - 1, y] != null) { grid[x - 1, y].position += new Vector3(-1, 0, 0); } // Update Block position
grid[x, y] = null;
}
}
public static void increaseColumn(int x)
{
for (int y = 0; y < h; y++)
{
if (grid[x, y] != null)
{
grid[x + 1, y] = grid[x, y]; // Move one towards right
if (grid[x + 1, y] != null) { grid[x + 1, y].position += new Vector3(1, 0, 0); } // Update Block position
grid[x, y] = null;
}
}
}
public static void decreaseRowsAbove(int y)
{
for (int i = y; i < h; i++)
decreaseRow(i);
}
public static void increaseRowsAbove(int y)
{
for (int i = h-2; i >= y; i--)
increaseRow(i);
}
public static void increaseRowsUnder(int y)
{
for (int i = y; i >= 0; i--)
increaseRow(i);
}
public static void decreaseRowsUnder(int y)
{
for (int i = 1; i <= y; i++)
decreaseRow(i);
}
public static void decreaseColumnsAbove(int x)
{
for (int i = x; i < w; i++)
decreaseColumn(i);
}
public static void decreaseColumnsUnder(int x)
{
for (int i = 1; i <= x; i++)
decreaseColumn(i);
}
public static void increaseColumnsUnder(int x)
{
for (int i = x; i >= 0; i--)
increaseColumn(i);
}
public static void increaseColumnsAbove(int x)
{
for (int i = w-2; i >= x; i--)
increaseColumn(i);
}
public static bool isRowFull(int y)
{
for (int x = 0; x < w; ++x)
if (grid[x, y] == null)
return false;
return true;
}
public static bool isColumnFull(int x)
{
int compteur = 0;
for (int y = 0; y < h; ++y)
{
if (grid[x, y] != null)
{ compteur++; }
}
if (compteur >= h - deletedRows.Count) { return true; }
else { return false; }
}
public static void clearDeleted()
{
for (int y = 0; y < h; ++y)
{
for (int x = 0; x < w; ++x)
{
if (deleted[x,y] != null)
{
Destroy(deleted[x, y].gameObject);
deleted[x, y] = null;
}
}
}
}
public static void deleteFullRowsAndColumns()
{
clearDeleted();
deletedRows = new List<int>();
for (int y = 0; y < 5; ++y)
{
if (isRowFull(y))
{
deleteRow(y);
deletedRows.Add(y);
increaseRowsUnder(y - 1);
}
}
for (int y = h-1; y >= 5; --y)
{
if (isRowFull(y))
{
deleteRow(y);
deletedRows.Add(y);
decreaseRowsAbove(y + 1);
}
}
deletedColumns = new List<int>(); ;
for (int x = 0; x < 5; ++x)
{
if (isColumnFull(x))
{
deleteColumn(x);
deletedColumns.Add(x);
increaseColumnsUnder(x - 1);
}
}
for (int x = w-1; x >= 5; --x)
{
if (isColumnFull(x))
{
deleteColumn(x);
deletedColumns.Add(x);
decreaseColumnsAbove(x + 1);
}
}
score += (deletedRows.Count + deletedColumns.Count) * (deletedRows.Count + deletedColumns.Count);
if (deletedRows.Count + deletedColumns.Count > 0)
{
if (score > PlayerPrefs.GetInt("Player Best Score"))
{
recordText = "\nNEW RECORD";
PlayerPrefs.SetInt("Player Best Score", score);
PlayerPrefs.Save();
}
spawner.updateScore();
}
foreach (Group grp in FindObjectsOfType<Group>())
{
if (grp.transform.childCount == 0)
{ Destroy(grp.gameObject); }
}
}
public static void restoreRow(int y)
{
for (int x = 0; x<w; x++)
{
if (deleted[x, y] != null)
{
grid[x, y] = deleted[x, y];
grid[x, y].renderer.enabled = true;
//deleted[x, y].position -= new Vector3(-10F, 0F, 0F);
deleted[x, y] = null;
}
}
}
public static void restoreColumn(int x)
{
for (int y = 0; y < h; y++)
{
if (deleted[x, y] != null)
{
grid[x, y] = deleted[x, y];
grid[x, y].renderer.enabled = true;
//deleted[x, y].position -= new Vector3(-10F, 0F, 0F);
deleted[x, y] = null;
}
}
}
public static void stock()
{
if (stockedGroup == null)
{
stockedGroup = actualGroup;
actualGroup = null;
FindObjectOfType<Spawner>().spawnNext(); // Spawn next Group
stockedGroup.recolorate();
stockedGroup.enabled = false;
stockedGroup.transform.position = new Vector3(1, h+1.75F, (float)-0.5);
stockedGroup.transform.localScale = new Vector3(0.5F, 0.5F, 1);
}
else
{
Group temp = stockedGroup;
stockedGroup = actualGroup;
actualGroup = temp;
stockedGroup.recolorate();
stockedGroup.enabled = false;
actualGroup.enabled = true;
stockedGroup.transform.position = new Vector3(1, h + 1.75F, (float)-0.5);
stockedGroup.transform.localScale = new Vector3(0.5F, 0.5F, 1);
actualGroup.transform.position = new Vector3((int)(w/2), (int)(h/2), (float)-0.5);
actualGroup.transform.localScale = new Vector3(1, 1, 1);
}
stocked = !stocked;
}
//public static bool lost()
//{
// if (stockedGroup != null && actualGroup != null)
// { return !actualGroup.canPlace() && !stockedGroup.canPlace(); }
// Debug.Log("Result : False");
// return false;
//}
public static void blit()
{
actualGroup.blit();
spawner.updateMessage();
}
public static void rotate()
{
actualGroup.rotate();
}
public static List<int> mySort(List<int> l)
{
List<int> l1 = new List<int>();
List<int> l2 = new List<int>();
l1 = l.FindAll(delegate(int i) { return i < 5; });
l2 = l.FindAll(delegate(int i) { return i >= 5; });
l1.Sort(delegate(int x, int y) { return -x.CompareTo(y); });
l2.Sort(delegate(int x, int y) { return x.CompareTo(y); });
l1.AddRange(l2);
return l1;
}
public static void cancel()
{
if (cancelActualGroup != null)
{
score = cancelScore;
spawner.updateScore();
if (deletedColumns.Count > 0)
{
deletedColumns = mySort(deletedColumns);
foreach (int y in deletedColumns)
{
if (y >= 5)
{
increaseColumnsAbove(y);
restoreColumn(y);
}
else
{
decreaseColumnsUnder(y);
restoreColumn(y);
}
}
}
if (deletedRows.Count > 0)
{
deletedRows = mySort(deletedRows);
foreach (int x in deletedRows)
{
if (x >= 5)
{
increaseRowsAbove(x);
restoreRow(x);
}
else
{
decreaseRowsUnder(x);
restoreRow(x);
}
}
}
if (stocked) // if the actualGroup had been stocked then it means that stockedGroup is supposed to be actualGroup
{
stock();
}
cancelRandomGroup = actualGroup;
cancelRandomGroup.enabled = false;
foreach (Transform child in cancelRandomGroup.transform)
{
child.renderer.enabled = false;
}
actualGroup = cancelActualGroup;
actualGroup.unblit();
actualGroup.enabled = true;
cancelActualGroup = null;
}
else
{
message = "Sorry, you can only revert once in a row !";
spawner.updateMessage();
}
}
public static void mute()
{
AudioListener.pause = true;
//AudioListener.volume = 100;
muted = true;
}
public static void unmute()
{
AudioListener.pause = false;
//AudioListener.volume = 0;
muted = false;
}
public static void quit()
{
if (quitCheck)
{
Application.LoadLevel("Menu");
}
else
{
spawner.quitText.text = "SURE ?";
quitCheck = true;
}
}
public static void unquit()
{
spawner.quitText.text = "QUIT";
quitCheck = false;
}
}
The group class :
using UnityEngine;
using System.Collections;
public class Group : MonoBehaviour
{
public Sprite block;
public Sprite original;
private Color colour;
// Use this for initialization
void Start()
{
Grid.actualGroup = this;
colour = RandomColour();
foreach (Transform child in transform)
{
child.renderer.material.color = colour;
child.localScale = new Vector3(0.75F, 0.75F, 1);
}
//Debug.Log("Verif defaite");
//if (Grid.lost()) { Debug.Log("\t\t\tDEFAAAAIIIIITE"); } //Application.LoadLevel("Menu"); }
}
public Color RandomColour()
{
int rnd = Random.Range(0, 6);
switch (rnd)
{
case 0: return Color.blue;
case 1: return Color.cyan;
case 2: return Color.white;
case 3: return Color.grey;
case 4: return Color.magenta;
case 5: return Color.yellow;
case 6: return Color.green;
default: return Color.white;
}
}
// Update is called once per frame
void Update()
{
colorate();
// Only if 1 finger :
//for (int i = 0; i < Input.touchCount; i++)
if (Input.touchCount == 1)
{
Touch touch = Input.GetTouch(0);
// -- Touch
// ------------------------------------------------
if ( touch.phase == TouchPhase.Ended || touch.phase == TouchPhase.Began || touch.phase == TouchPhase.Moved )
{
// Touch are screens location. Convert to world
Vector3 position = Camera.main.ScreenToWorldPoint(touch.position);
//Grid.message = "Touch position : " + touch.position + ", world position : " + position;
if (position.y >= -1 && position.y < Grid.h )
{
// Reset quit
Grid.unquit();
// Effect for feedback
position.z = (float)-0.5;
transform.position = Grid.roundVec3(position);
validatePos();
}
}
}
#if UNITY_EDITOR
if (Input.GetMouseButtonDown(0))
{
Vector3 position = Camera.main.ScreenToWorldPoint(Input.mousePosition);
//Grid.message = "Touch position : " + touch.position + ", world position : " + position;
if (position.y >= -1 && position.y < Grid.h)
{
// Reset quit
Grid.unquit();
// Effect for feedback
position.z = (float)-0.5;
transform.position = Grid.roundVec3(position);
validatePos();
}
}
#endif
}
public void colorate()
{
foreach (Transform child in transform)
{
Vector2 v = Grid.roundVec2(child.position);
{ child.renderer.material.color = colour; }
if (Grid.grid[(int)v.x, (int)v.y] != null)
{ child.renderer.material.color = Color.red; }
}
}
public void recolorate()
{
foreach (Transform child in transform)
{
{ child.renderer.material.color = colour; }
}
}
public bool canBlit()
{
foreach (Transform child in transform)
{
Vector2 v = Grid.roundVec2(child.position);
if (Grid.grid[(int)v.x, (int)v.y] != null)
{ return false; }
}
return true;
}
public void blit()
{
if (canBlit())
{
// Blit children
foreach (Transform child in transform)
{
Vector2 v = Grid.roundVec2(child.position);
child.position += new Vector3(0, 0, (float)0.5);
Grid.grid[(int)v.x, (int)v.y] = child;
child.renderer.material.color = colour;
child.localScale = new Vector3(1, 1, 1);
child.GetComponent<SpriteRenderer>().sprite = block;
}
// Disable script
enabled = false;
// Save groups to enventually revert later
Grid.cancelActualGroup = this;
Grid.cancelScore = Grid.score;
// Clear filled horizontal lines
Grid.deleteFullRowsAndColumns();
// Spawn next Group
FindObjectOfType<Spawner>().spawnNext();
// Reset stocked
Grid.stocked = false;
// Reset message
Grid.message = "";
}
}
public void unblit()
{
foreach (Transform child in transform)
{
Vector2 v = Grid.roundVec2(child.position);
child.position -= new Vector3(0, 0, (float)0.5);
Grid.grid[(int)v.x, (int)v.y] = null;
child.localScale = new Vector3(0.75F, 0.75F, 1);
child.GetComponent<SpriteRenderer>().sprite = original;
}
}
public static int min ( int a, int b)
{
if (a < b) { return a; }
else { return b; }
}
bool isValidGridPos()
{
foreach (Transform child in transform)
{
Vector2 v = Grid.roundVec2(child.position);
// Not inside Border?
if (!Grid.insideBorder(v))
return false;
}
return true;
}
void validatePos()
{
bool invalid = true;
while (invalid)
{
invalid = false;
foreach (Transform child in transform)
{
Vector2 v = Grid.roundVec2(child.position);
if (v.x >= Grid.w) { transform.position += new Vector3(-1, 0, 0); invalid = true; }
if (v.x < 0) { transform.position += new Vector3(1, 0, 0); invalid = true; }
if (v.y >= Grid.h) { transform.position += new Vector3(0, -1, 0); invalid = true; }
if (v.y < 0) { transform.position += new Vector3(0, 1, 0); invalid = true; }
}
}
}
//public bool canPlace()
//{
// for (int k = 0; k < 4; k++)
// {
// for (int i = 0; i < Grid.w; i++)
// {
// for (int j = 0; j < Grid.h; j++)
// {
// if (canPlace(i, j)) { Debug.Log("CAN PLACE "+transform+" AT : ("+i+","+j+")"); return true; }
// }
// }
// transform.Rotate(0, 0, -90);
// }
// return false;
//}
//public bool canPlace(int x, int y)
//{
// foreach (Transform child in transform)
// {
// Vector2 v = Grid.roundVec2(child.localPosition);
// v.x += x;
// v.y += y;
// if (v.x >= Grid.w || v.x < 0 || v.y >= Grid.h || v.y < 0) { Debug.Log("\t Check (" + v.x + "," + v.y + ") : HORS LIMITES"); return false; }
// if (Grid.grid[(int)v.x, (int)v.y] != null) { Debug.Log("\t Check (" + v.x + "," + v.y + ") : PEUT PAS POSER"); return false; }
// }
// return true;
//}
public void rotate()
{
// Rotate
transform.Rotate(0, 0, -90);
validatePos();
colorate();
}
}
And finally the Spawner class :
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
public class Spawner : MonoBehaviour
{
// Groups
public Text buttonText;
public Text scoreText;
public Text messageText;
public Text muteText;
public Text quitText;
public GameObject[] groups;
public void spawnNext()
{
if (Grid.cancelRandomGroup != null)
{
Grid.actualGroup = Grid.cancelRandomGroup;
Grid.cancelRandomGroup = null;
Grid.actualGroup.enabled = true;
foreach (Transform child in Grid.actualGroup.transform)
{
child.renderer.enabled = true;
}
}
else
{
// Random Index
int i = Random.Range(0, Group.min((groups.Length), (int)(6 + (Grid.score) / 6)));
// Spawn Group at current Position
Instantiate(groups[i], transform.position, Quaternion.identity);
}
}
void Start()
{
// Mute if set to true
if (Grid.muted) { Grid.mute(); muteText.text = "UNMUTE"; }
else { muteText.text = "MUTE"; }
// Refer to Spawner in Grid
Grid.spawner = this;
// Spawn initial Group
spawnNext();
// Set score to 0
scoreText.text = "SCORE\n<color=red><size=20>" + Grid.score + "</size></color><color=cyan>\n" + Grid.recordText + "</color>";
}
void Update()
{
}
public void updateScore()
{
scoreText.text = "SCORE\n<color=red><size=20>" + Grid.score + "</size></color><color=cyan>\n" + Grid.recordText + "</color>";
}
public void updateMessage()
{
messageText.text = Grid.message;
}
public void demandeStock() { buttonText.text = "" ; Grid.stock(); }
public void demandeTourner() { Grid.rotate(); }
public void demandeBlit() { Grid.blit(); }
public void demandeCancel() { Grid.cancel(); }
public void demandeQuit() { Grid.quit(); }
public void demandeMute()
{
if (Grid.muted) { Grid.unmute(); muteText.text = "MUTE"; }
else { Grid.mute(); muteText.text = "UNMUTE"; }
}
}
I'm pretty sure it's not fully optimized (for example when you use game functions like "blit" when you want to place your piece) but those functions run fast ! Lags appear when you move the Group (so it calls the Group.Update()) or even when you don't do anything (i know 'cause i hear the music lagging).
You'll better to give us full project. It'll be much less time consu$$anonymous$$g to use profiler and debugging tools ins$$anonymous$$d to get through this infinite scrolls of code :)
It's already taking about 700 $$anonymous$$B... How can i give it to you ?