I'm trying to understand tileSystem build in Unity, and i don't know how to stop animation in AnimatedTiles.
Once animation is started, there is no way i can think of to stop this. I'm working on Unity 2018.3.2f1, but i think that TileSystem is similar in next versions.
Only code in AnimatedTile handling animation is:
public override void GetTileData(Vector3Int location, ITilemap tileMap, ref TileData tileData)
{
tileData.transform = Matrix4x4.identity;
tileData.color = Color.white;
if (m_AnimatedSprites != null && m_AnimatedSprites.Length > 0)
{
tileData.sprite = m_AnimatedSprites[0];
tileData.colliderType = m_TileColliderType;
}
}
public override bool GetTileAnimationData(Vector3Int location, ITilemap tileMap, ref TileAnimationData tileAnimationData)
{
if (m_AnimatedSprites.Length > 0)
{
tileAnimationData.animatedSprites = m_AnimatedSprites;
tileAnimationData.animationSpeed = Random.Range(m_MinSpeed, m_MaxSpeed);
tileAnimationData.animationStartTime = m_AnimationStartTime;
return true;
}
return false;
}
I want to stop animation after some time (like 3 seconds) or after last frame. Any help would be appritiated!
So after some time a got workaround and it looks like this :
public class TileBump : MonoBehaviour
{
public Transform m_GridParent;
public GameObject m_TileMap_Prefab;
public AnimatedTile m_tilePrefabAnimated;
public Tile m_tilePrefabStatic;
private Tilemap map;
void Start()
{
StartCoroutine(EStart());
}
public IEnumerator EStart()
{
GameObject t = Instantiate(m_TileMap_Prefab, m_GridParent);
map = t.GetComponent<Tilemap>();
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
map.SetTile(new Vector3Int(i, j, 0), m_tilePrefabAnimated);
StartCoroutine(Operation(new Vector3Int(i, j, 0)));
yield return new WaitForSeconds(0.3f);
}
}
}
public IEnumerator Operation(Vector3Int x)
{
yield return new WaitForSeconds(m_tilePrefabAnimated.m_AnimatedSprites.Length / m_tilePrefabAnimated.m_AnimationSpeed);
map.SetTile(x, m_tilePrefabStatic);
}
}
BUT. What i understood here is that tiles are not for that. Every tile in TileMap refer to ScriptableObject, so every animation will be same in every frame.
However if someone need this kind of effect, its one way to do it.
Related
I am working on small game similar to angry birds since I am new to both unity
and C#. I want to make load and unlock next level if all enemies are dead and check if new level exist
(if not) return back to Level selection scene.
This is what I tried:
LevelScript
using UnityEngine;
using UnityEngine.SceneManagement;
public class LevelScript : MonoBehaviour
{
[SerializeField] string _nextLevelName;
Monster[] _monsters;
void OnEnable()
{
_monsters = FindObjectsOfType<Monster>();
}
private void Update()
{
int currentLevel = SceneManager.GetActiveScene().buildIndex ;
if (currentLevel >= PlayerPrefs.GetInt("levelsUnlocked"))
{
PlayerPrefs.SetInt("levelsUnlocked", currentLevel );
}
if (MonsterAreAllDead())
{
GoToNextLevel();
}
Debug.Log("Level" + PlayerPrefs.GetInt("levelsUnlocked") + "UNLOCKED");
}
public void Pass()
{
int currentLevel = SceneManager.GetActiveScene().buildIndex;
if (currentLevel >= PlayerPrefs.GetInt("levelsUnlocked") )
{
PlayerPrefs.SetInt("levelsUnlocked", currentLevel + 1);
};
}
bool MonsterAreAllDead()
{
foreach (var monster in _monsters)
{
if (monster.gameObject.activeSelf)
return false;
}
return true;
}
void GoToNextLevel()
{
Debug.Log("Go to next level" + _nextLevelName);
SceneManager.LoadScene(_nextLevelName);
}
}
and Level Manager
public class LevelManager : MonoBehaviour
{
int levelsUnlocked;
public Button[] buttons;
void Start()
{
levelsUnlocked = PlayerPrefs.GetInt("levelsUnlocked", 1);
for (int i = 0; i < buttons.Length; i++)
{
buttons[i].interactable = false;
}
for (int i = 0; i < levelsUnlocked; i++)
{
buttons[i].interactable = true;
}
}
public void LoadLevel(int levelIndex)
{
SceneManager.LoadScene(levelIndex);
}
I got these 2 scripts and I attached both canvas and my buttons and Level script to my levels.
Problem is that every level gets unlocked at begin and after completeing levels automatically
it want to go to next level whic is not exist yet.
Pls help me. Sorry if my question is stupid and for bad english.
You should make an array of scene in your LevelManager that know all your levels (in order)
and for getting next level you can get the position of your actual scene in the array and check the next one.
somethink like
pseudocode :
[Serialized]
Scene[] AllScenes
void GoToNextLevel()
{
int currentLevelPos = AllScenes.IndexOf(currentScene);
if (AllScenes[currentLevelPos + 1] != null)
Load Next Level
else
Go To Level Menu
}
Good day everyone, currently I am developing a simple 2D game using SWINGAME. I have set a collision between 2 objects. So when they collide, I want to temporarily make one of them invisible for a certain time. I am stuck about the time component, let's say I want the object to be invisible for 3 seconds after that it will change back to the default object. Below are the two images, if the collision is true then it will display image2, or else display image1. BTW I use a different image to indicate the invisibility. Here's my code.
Player class:
public void Draw ()
{
if (invisible == true) {
if(elapsedTime <= 3.0){
elapsedTime += elapsedTime;
SwinGame.DrawBitmap ("image2.png", (float)X, (float)Y);
}
}else {
elapsedTime = 0;
SwinGame.DrawBitmap ("image1.png", (float)X, (float)Y);
}
}
public bool Invisible {
get { return invisible; }
set { invisible = value; }
}
Object Collision class :
{... //Some codes
for(int i = 0; i < _obstacles.Count; i++)
{
if (_obstacles [i] is Invisible) {
p.Invisible = true;
p.Draw ();
}
}
//Some codes ...}
This should help you to calculate the time accurately by using the StopWatch class:
//somewhere in your code
Stopwatch sw = new Stopwatch();
sw.Start();
public void Draw ()
{
if (invisible == true) {
if(sw.ElapsedMilliseconds <= 3000){
SwinGame.DrawBitmap ("image2.png", (float)X, (float)Y);
}
}else {
sw.Restart();
SwinGame.DrawBitmap ("image1.png", (float)X, (float)Y);
}
}
thanks for reading.
I'm currently building a small memory card game in Unity using C#. I have the main portion of code finished but when I press the play button on a certain scene Unity freezes.
I believe it is due to an infinite While loop, but I can not find the issue. I would really appreciate any help anyone can offer. I will leave my code below. Thanks in advance.
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
using UnityEngine;
public class Pairs : MonoBehaviour {
public Sprite[] face; //array of card faces
public Sprite back;
public GameObject[] deck; //array of deck
public Text pairsCount;
private bool deckSetUp = false;
private int pairsLeft = 13;
// Update is called once per frame
void Update () {
if (!deckSetUp)
{
SetUpDeck();
}
if (Input.GetMouseButtonUp(0)) //detects left click
{
CheckDeck();
}
}//Update
void SetUpDeck()
{
for (int ix = 0; ix < 2; ix++)
{
for(int i = 1; i < 14; i++)//sets up card value (2-10 JQKA)
{
bool test = false;
int val = 0;
while (!test)
{
val = Random.Range(0, deck.Length);
test = !(deck[val].GetComponent<Card>().SetUp);
}//while
//sets up cards
deck[val].GetComponent<Card>().Number = i;
deck[val].GetComponent<Card>().SetUp = true;
}//nested for
}//for
foreach (GameObject crd in deck)
{
crd.GetComponent<Card>().setUpArt();
}
if (!deckSetUp)
{
deckSetUp = true;
}
}//SetUpDeck
public Sprite getBack()
{
return back;
}//getBack
public Sprite getFace(int i)
{
return face[i - 1];
}//getFace
void CheckDeck()
{
List < int > crd = new List<int>();
for(int i = 0; i < deck.Length; i++)
{
if(deck[i].GetComponent<Card>().State == 1)
{
crd.Add(i);
}
}
if(crd.Count == 2)
{
CompareCards(crd);
}
}//CheckDeck
void CompareCards(List<int> crd)
{
Card.NO_TURN = true; //stops cards turning
int x = 0;
if(deck[crd[0]].GetComponent<Card>().Number ==
deck[crd[1]].GetComponent<Card>().Number)
{
x = 2;
pairsLeft--;
pairsCount.text = "PAIRS REMAINING: " + pairsLeft;
if(pairsLeft == 0) // goes to home screen when game has been won
{
SceneManager.LoadScene("Home");
}
}
for(int j = 0; j < crd.Count; j++)
{
deck[crd[j]].GetComponent<Card>().State = x;
deck[crd[j]].GetComponent<Card>().PairCheck();
}
}//CompareCards
}
I believe the issue lies in the while(!test) but i do not know why test never become true.
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine;
public class Card : MonoBehaviour {
public static bool NO_TURN = false;
[SerializeField]
private int cardState; //state of card
[SerializeField]
private int cardNumber; //Card value (1-13)
[SerializeField]
private bool _setUp = false;
private Sprite back; //card back (Green square)
private Sprite face; //card face (1-10 JQKA)
private GameObject pairsManager;
void Begin()
{
cardState = 1; //cards face down
pairsManager = GameObject.FindGameObjectWithTag("PairsManager");
}
public void setUpArt()
{
back = pairsManager.GetComponent<Pairs>().getBack();
face = pairsManager.GetComponent<Pairs>().getFace(cardNumber);
turnCard();//turns the card
}
public void turnCard() //handles turning of card
{
if (cardState == 0)
{
cardState = 1;
}
else if(cardState == 1)
{
cardState = 0;
}
if (cardState == 0 && !NO_TURN)
{
GetComponent<Image>().sprite = back; // shows card back
}
else if (cardState == 1 && !NO_TURN)
{
GetComponent<Image>().sprite = face; // shows card front
}
}
//setters and getters
public int Number
{
get {return cardNumber;}
set { cardNumber = value;}
}
public int State
{
get { return cardState; }
set { cardState = value; }
}
public bool SetUp
{
get { return _setUp; }
set { _setUp = value; }
}
public void PairCheck()
{
StartCoroutine(pause ());
}
IEnumerator pause()
{
yield return new WaitForSeconds(1);
if (cardState == 0)
{
GetComponent<Image>().sprite = back;
}
else if (cardState == 1)
{
GetComponent<Image>().sprite = face;
}
}
}
Thank you for reading, I will post a link to the github repository if that helps.
github repository
Your deck array has at least one card in it that has _setUp set to true which would make it go in a infinite loop.
The reason it goes in a infinite loop is because it will have set all available _setUp to true and it would keep looking for _setUp that are set to false and it will never find any.
The reason you need at least 26 object that have _setUp to false is because in the nested for loop you loop 13 times and then you do that twice which gives a total of 26 loops. So you need at least 26 objects.
What you can do to make sure that they're all false is to set them all to false before entering the for loop
for(int i = 0; i < deck.Length; i++)
{
deck[i].GetComponent<Card>().SetUp = false;
}
I just made a png animation sequence that fires when the object is found but on the first loop it takes a lot to load, is there a way to load it faster?
The animation consist of 500 pngs loaded in a sprite the total size of all is about 180Mb, and the code that I am using is very simple:
DefaultTrackableEventHandler.cs:
public SpriteRenderer sr;
public int i = 0;
void Update ()
{
i++;
i = (i > 100) ? 0 : i;
sr.sprite = Resources.Load<Sprite> ("fractal/03_" + i.ToString ("D5"));
}
btw i'm a complete noob on unity programming so please forgive me if i'm missing something obvious, thanks
The problem is that you are loading sprite every frame. It will effect performance. Do the loading once in the Start function and store the result in an array. You can then use that array in the Update function.
const int ANIM_SIZE = 500;
Sprite[] animSprites;
public SpriteRenderer sr;
public int i = 0;
void Start()
{
for (int i = 0; i < ANIM_SIZE; i++)
{
animSprites[i] = Resources.Load<Sprite>("fractal/03_" + i.ToString("D5")) as Sprite;
}
}
void Update()
{
i++;
i = (i > 100) ? 0 : i;
sr.sprite = animSprites[i];
}
Since you are loading many files, you should use Resources.LoadAsync instead. That will let you load the data over frame and prevent any possible freezing when loading the sprites.
const int ANIM_SIZE = 500;
Sprite[] animSprites;
public SpriteRenderer sr;
public int i = 0;
bool isDoneLoading = false;
IEnumerable Start()
{
for (int i = 0; i < ANIM_SIZE; i++)
{
ResourceRequest rs = Resources.LoadAsync<Sprite>("fractal/03_" + i.ToString("D5"));
yield return rs;
animSprites[i] = rs.asset as Sprite;
}
isDoneLoading = true;
}
void Update()
{
if (isDoneLoading)
{
i++;
i = (i > 100) ? 0 : i;
sr.sprite = animSprites[i];
}
}
Finally, don't use any of these solutions mentioned above. They are just there because that's what you asked for. Use the Unity's Animation/Animator's tool to do this. You can learn more about this here.
I'm working on a tile game, but I ran into a big problem.
The idea is that if a tile is selected it turns red and a few tiles surounded turn blue. if you click one of the blue ones the unit of the slected one is transported to that one , but every time a do this my selected tile has a NullException . I can use the Tile itself, but non of the values or voids it contains which always return null. I'd be very thankfull if someone could pinpoint the problem
Code in order of likelyhood of problem
Part of Update():
while (j < baseLayer.MapDimensions.Y)
{
while (i < baseLayer.MapDimensions.X)
{
if (moveToMap[i, j] && standardCheck.Click(new Rectangle(i * 100, j * 100, 100, 100), mouseState, prevMouseState))
{
tileArray[i, j].changeColor = Color.Gold;
tileArray[i, j].MoveTo(tileArray[(int)selectedTile.X, (int)selectedTile.Y].Position,
tileArray[(int)selectedTile.X, (int)selectedTile.Y].getUnit);// problem occors here
//problem occurres here
}
else
{
tileArray[i, j].Checkclick(mouseState, prevMouseState, relativeMousePosition);
if (tileArray[i, j].ReturnIfSelected)
{
selectedTile = new Vector2(i, j);
}
else if (selectedTile == new Vector2(i, j))
selectedTile = new Vector2(200, 200);
}
i++;
}
i = 0;
j++;
}
j = 0;
Tile class
public Unit unit = new Unit();
public Unit Building = new Unit();
public Unit getUnit
{
get { return unit; }
}
public Color tileColor = Color.White;
public Vector2 position;
public Vector2 drawPosition;
public int[] map { set; get; } = new int[4];
public bool selected = false;
public void LoadContent(Vector2 _position ,int[,,] _map)
{
}
public void Checkclick(MouseState mS, MouseState prevMS,Vector2 matrixPosition)
{
}
}
public bool checkifselectable()
{
}
public void Update(int[,,] _map)
{
unit.Reset();
}
public void MoveTo(Vector2 oldTile,Unit _unit)
{
int change = 0;
if (oldTile.X - position.X != 0)
change = (int)(oldTile.X - position.X);
if (oldTile.Y - position.Y !=0)
change = (int)(oldTile.Y - position.Y);
unit = _unit;
map[2] = _unit.ID;
if (change>= 0)
unit.RemainingMoveSpeed = change;
if (change < 0)
unit.RemainingMoveSpeed = -change;
}
Thanks already for your time
The Young Programmer
Maybe you have NullReferenceException. In this case, this exception is thrown when there is an attempt to dereference a null object reference. This might help you https://msdn.microsoft.com/en-us/library/system.nullreferenceexception%28v=vs.110%29.aspx.