GetInt can only be called from main thread? - c#

This question HAS been answered before but I tried everything and it didn't work.
I have this int in a flappy bird clone where you collect coins and it goes up by one kinda like a coin counter.
Every time you collect a coin it does
PlayerPrefs.SetInt("coin", coin);
coin+=1
PlayerPrefs.Save();
And when a new coin spawns I do this in the very start of the class (not in void Start() )
private int coin = PlayerPrefs.GetInt("coin");
And everything worked fine.
I even tested it by putting a print in console code and it counted down the coins just fine!
But I also have this other scene called Shop, where you can spend those coins. However I made like a little way to draw those integers, that should work fine, BUT I get an error that getInt can only be called from main thread, which is dumb because I cant put it in the start / awake / update method, because when I do so I get a ton more errors.
What do I do? I want to call the coin integer that I saved using playerprefs in that scene in order to draw the amount of coins
I try calling it like this: private int coin = PlayerPrefs.GetInt("coin");
but I get an error, even though I don't get one when I call it in the actual game scene.
Full code:
using UnityEngine;
using System.Collections;
public class SpriteScript : MonoBehaviour
{
private int coin = PlayerPrefs.GetInt("coin");
public Sprite num1;
public Sprite num2;
public Sprite num3;
public Sprite num4;
public Sprite num5;
public Sprite num6;
public Sprite num7;
public Sprite num8;
public Sprite num9;
public Sprite num0;
private int spritenum;
private SpriteRenderer spriteRenderer;
// Use this for initialization
void Start()
{
spriteRenderer = GetComponent<SpriteRenderer>(); // we are accessing the SpriteRenderer that is attached to the Gameobject
if (spriteRenderer.sprite == null) // if the sprite on spriteRenderer is null then
spriteRenderer.sprite = num0; // set the sprite to sprite1
}
// Update is called once per frame
void Update()
{
if (coin < 10)
{
spritenum = coin;
if (coin > 10)
{
if (coin < 20)
{
spritenum = coin - 10;
}
if (coin > 20)
{
if (coin < 30)
{
spritenum = coin - 20;
}
if (coin > 30)
{
if (coin < 40)
{
spritenum = coin - 30;
}
if (coin > 40)
{
if (coin < 50)
{
spritenum = coin - 40;
}
if (coin == 50)
{
spritenum = coin - 50;
}
}
}
}
}
}
ChangeTheDamnSprite();
}
void ChangeTheDamnSprite()
{
if (spritenum == 0)
{
spriteRenderer.sprite = num0;
}
if (spritenum == 1)
{
spriteRenderer.sprite = num1;
}
if (spritenum == 2)
{
spriteRenderer.sprite = num2;
}
if (spritenum == 3)
{
spriteRenderer.sprite = num3;
}
if (spritenum == 4)
{
spriteRenderer.sprite = num4;
}
if (spritenum == 5)
{
spriteRenderer.sprite = num5;
}
if (spritenum == 6)
{
spriteRenderer.sprite = num6;
}
if (spritenum == 7)
{
spriteRenderer.sprite = num7;
}
if (spritenum == 8)
{
spriteRenderer.sprite = num8;
}
if (spritenum == 9)
{
spriteRenderer.sprite = num9;
}
}
}
By the way if you're wondering, it pretty much looks how many coins you have and replaces the sprite to match the number, please don't question the stupid way I used to draw the coin amount, can you please help me fix the problem? As I said I tried putting it in start method, nothing happens, instead more errors.
Oh and im building to android if that changes anything

I suspect you're approaching this problem the wrong way. May I suggest you create a new class called CoinController : Monobehaviour and attach it to your player.
public class CoinController: MonoBehaviour {
int coins;
public int Coins {
get {
return coins;
}
}
public Sprite GetSprite() {
//logic for determining sprite based on number of coins here
}
public void AddCoins(int num) {
coins += num;
}
public void SpendCoins(int num) {
coins -= num;
}
}
CoinController coinController = GetComponent<CoinController>();
coinController.AddCoins(5);
coinController.SpendCoins(2);
//in update method
Sprite thisSprite = coinController.GetSprite();
//draw sprite logic, not sure how you're doing this, but DrawCoinSprite would be some method that updates which sprite to draw
DrawCoinSprite(thisSprite);
I hope that helps!

Related

Pawn not moving on a board game

I have a board game project in unity, where six players roll a dice, and go forward using a waypoint system on a really basic board game (2d). I found some code on YouTube, and adapted it to my project. everything goes pretty well, except for the fact that the pawns don't move forward. They set correctly to the first waypoint, they are selected to move, but don't. I suspect that my transform.position don't work, but I can't understand why. I'm really new to c# and unity, so I can't find the problem.
Here is my pawn movement code :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FollowThePath : MonoBehaviour
{
public Transform[] waypoints;
[SerializeField]
private float moveSpeed = 1f;
[HideInInspector]
public int waypointIndex = 0;
public bool moveAllowed = false;
// Start is called before the first frame update
private void Start()
{
transform.position = waypoints[waypointIndex].transform.position;
}
// Update is called once per frame
private void Update()
{
if (moveAllowed)
Move();
}
private void Move(){
if (waypointIndex <= waypoints.Length -1)
{
transform.position = Vector2.MoveTowards(transform.position,
waypoints[waypointIndex].transform.position,
moveSpeed * Time.deltaTime);
Debug.Log(transform.position);
if (transform.position == waypoints[waypointIndex].transform.position)
{
waypointIndex += 1;
}
}
}
}
I think the problem is at the end of this script.
I also have a dice script :
using System.Collections;
using UnityEngine;
public class Dice : MonoBehaviour {
// Array of dice sides sprites to load from Resources folder
private Sprite[] diceSides;
// Reference to sprite renderer to change sprites
private SpriteRenderer rend;
// Select the player's turn
private int whosturn = 1;
//Check if we can roll the dice
private bool coroutineallowed = true;
// Use this for initialization
private void Start () {
// Assign Renderer component
rend = GetComponent<SpriteRenderer>();
// Load dice sides sprites to array from DiceSides subfolder of Resources folder
diceSides = Resources.LoadAll<Sprite>("DiceSides/");
}
// If you left click over the dice then RollTheDice coroutine is started
private void OnMouseDown()
{
if (!GameControl.gameOver && coroutineallowed)
StartCoroutine("RollTheDice");
}
// Coroutine that rolls the dice
private IEnumerator RollTheDice()
{
// Turn coroutineallowed off
coroutineallowed = false;
// Variable to contain random dice side number.
// It needs to be assigned. Let it be 0 initially
int randomDiceSide = 0;
// Loop to switch dice sides ramdomly
// before final side appears. 10 iterations here.
for (int i = 0; i <= 10; i++)
{
// Pick up random value from 0 to 5 (All inclusive)
randomDiceSide = Random.Range(0, 6);
// Set sprite to upper face of dice from array according to random value
rend.sprite = diceSides[randomDiceSide];
// Pause before next iteration
yield return new WaitForSeconds(0.05f);
}
GameControl.diceSideThrown = randomDiceSide + 1;
if (whosturn == 1){
GameControl.MovePlayer(1);
}
if (whosturn == 2){
GameControl.MovePlayer(2);
}
if (whosturn == 3){
GameControl.MovePlayer(3);
}
if (whosturn == 4){
GameControl.MovePlayer(4);
}
if (whosturn == 5){
GameControl.MovePlayer(5);
}
if (whosturn == 6){
GameControl.MovePlayer(6);
whosturn -= 6;
}
if (whosturn <= 5){
whosturn += 1;
}
coroutineallowed = true;
}
}
and a Game control script :
using System.Collections;
using UnityEngine;
public class Dice : MonoBehaviour {
// Array of dice sides sprites to load from Resources folder
private Sprite[] diceSides;
// Reference to sprite renderer to change sprites
private SpriteRenderer rend;
// Select the player's turn
private int whosturn = 1;
//Check if we can roll the dice
private bool coroutineallowed = true;
// Use this for initialization
private void Start () {
// Assign Renderer component
rend = GetComponent<SpriteRenderer>();
// Load dice sides sprites to array from DiceSides subfolder of Resources folder
diceSides = Resources.LoadAll<Sprite>("DiceSides/");
}
// If you left click over the dice then RollTheDice coroutine is started
private void OnMouseDown()
{
if (!GameControl.gameOver && coroutineallowed)
StartCoroutine("RollTheDice");
}
// Coroutine that rolls the dice
private IEnumerator RollTheDice()
{
// Turn coroutineallowed off
coroutineallowed = false;
// Variable to contain random dice side number.
// It needs to be assigned. Let it be 0 initially
int randomDiceSide = 0;
// Loop to switch dice sides ramdomly
// before final side appears. 10 iterations here.
for (int i = 0; i <= 10; i++)
{
// Pick up random value from 0 to 5 (All inclusive)
randomDiceSide = Random.Range(0, 6);
// Set sprite to upper face of dice from array according to random value
rend.sprite = diceSides[randomDiceSide];
// Pause before next iteration
yield return new WaitForSeconds(0.05f);
}
GameControl.diceSideThrown = randomDiceSide + 1;
if (whosturn == 1){
GameControl.MovePlayer(1);
}
if (whosturn == 2){
GameControl.MovePlayer(2);
}
if (whosturn == 3){
GameControl.MovePlayer(3);
}
if (whosturn == 4){
GameControl.MovePlayer(4);
}
if (whosturn == 5){
GameControl.MovePlayer(5);
}
if (whosturn == 6){
GameControl.MovePlayer(6);
whosturn -= 6;
}
if (whosturn <= 5){
whosturn += 1;
}
coroutineallowed = true;
}
}

My script that uses a raycast to deal damage over time is causing Unity to crash. Anyone know why?

I have created a script that makes an enemy deal damage over time to a player from a raycast but it is making Unity crash once I'm in the required range for the enemy to move and deal damage. Anyone know why?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyMove : MonoBehaviour
{
public Transform target;
public Transform player;
public float enemySpeed;
public int moveTrigger = 1;
public bool isAttacking;
public int AttackTirgger;
public float distanceFromPlayer;
void Update()
{
distanceFromPlayer = Vector3.Distance(target.transform.position, player.transform.position);
if (distanceFromPlayer <= 10 && moveTrigger == 1)
{
transform.LookAt(target);
if (!isAttacking)
StartCoroutine(EnemyDamage());
}
if (distanceFromPlayer < 10 && moveTrigger == 1 && distanceFromPlayer > 3)
{
transform.Translate(Vector3.forward * enemySpeed * Time.deltaTime);
}
}
IEnumerator EnemyDamage()
{
isAttacking = true;
while (distanceFromPlayer <= 10)
{ // in range
RaycastHit PlayerHit;
if (Physics.Raycast(target.transform.position, target.transform.forward, out PlayerHit))
{
Target target = PlayerHit.transform.GetComponent<Target>();
if (target != null)
{
GlobalHealth.playerHealth -= 1;
yield return new WaitForSeconds(2);
}
}
}
isAttacking = false; // out of range
yield return null;
}
}
If I try to explain what your code does (in pseudo code) :
if(distance_to_target < 10)
{
 lookAt(taget)
 move_forward() //so you get closer from the target
 while(distance < 10)
{
do_stuff() //the stuff doesn't change distance
}
}
When your distance became smaller than 10, and due to your transform.Translate() it stay < 10 forever, so here you have a while(true) → makes unity crash

Unity OnTriggerEnter

I want my Player to collide with the object capsule.
This action should destroy the capsule and add a speed value of 10 to the player.
But this code is not working :
public class PlayerController : MonoBehaviour {
public KeyCode moveL;
public KeyCode moveR;
public float horizontal = 0;
public int laneNum = 2;
public string controllocked = "n";
public float speed;
void Update ()
{
GetComponent<Rigidbody>().velocity = new Vector3(horizontal, GM.verticalVelocity, speed);
if ((Input.GetKeyDown(moveL)) && (laneNum > 1) && (controllocked == "n"))
{
horizontal = -2;
StartCoroutine(StopSlide());
laneNum = laneNum - 1;
controllocked = "y";
}
else if ((Input.GetKeyDown(moveR)) && (laneNum < 3) && (controllocked =="n"))
{
horizontal = 2;
laneNum = laneNum + 1;
StartCoroutine(StopSlide());
controllocked = "y";
}
}
void OnCollisionEnter(Collision other)
{
if(other.gameObject.tag == "lethal")
{
Destroy(gameObject);
}
if (other.gameObject.name == "Capsule")
{
Destroy(other.gameObject);
speed = 10;
}
}
IEnumerator StopSlide()
{
yield return new WaitForSeconds(.5f);
horizontal = 0;
controllocked = "n";
}
What I've tried so far is speed += 10 and speed++ neither works.
Well, at first try to check your player, What collider type that you use in the player?
Make sure you check trigger in the collider component and add rigidbody into it.
The capsule object must have rigidbody on it.
Hope it help.
Use OnTriggerEnter(Collider collider) if you want to use triggerenter. Collision enter work if trigger is not checked

Blink GameObject

I am a bit of a noob to programming and I am trying to make a GameObject , deactivate and reactivate over a set amount of seconds.For example I want my star to slowly blink before it goes away, to create a cool looking effect. If there is a better way of using this method done with out using SetActive(false) and what not , please feel free to give me your method - This is my code , Sorry if its messy i gotta get better at this but i will in due time
Thanks guys
//Timers
public float ScoreTimer;
public float[] TimeMarkStamp;
//Scoring
public int totalCollStars;
[Space]
public int maxStars = 5;
public GameObject[] StarImages;
// Use this for initialization
void Start()
{
}
// Update is called once per frame
void Update()
{
ScoreTimer += Time.deltaTime;
if (ScoreTimer <= TimeMarkStamp[0])
{
Debug.Log("It works");
StarImages[0].SetActive(false);
}
else if (ScoreTimer <= TimeMarkStamp[1])
{
Debug.Log("It workds" + TimeMarkStamp[1]);
StarImages[1].SetActive(false);
}
else if (ScoreTimer <= TimeMarkStamp[2])
{
Debug.Log("It works" + TimeMarkStamp[2]);
StarImages[2].SetActive(false);
}
else if (ScoreTimer <= TimeMarkStamp[3])
{
//This is not working
InvokeRepeating("flickerEffect", 3f, 1f);
}
}
void flickerEffect()
{
bool flickCheck = false;
if (flickCheck == false)
{
StarImages[3].SetActive(true);
flickCheck = true;
}
else if (flickCheck == true)
{
StarImages[3].SetActive(false);
flickCheck = false;
}
}
}
If there is a better way of using this method done with out using
SetActive(false) and what not
Yes, there is a better way, other than using the SetActive function. You should change the alpha color of the GameObject from 0 to 1 back and forth. After that you can then disable the GameObject with SetActive. This saves how much garbage would have been generated when repeatedly calling the SetActive function.
If this is a 3D GameObject, change the Rendering Mode from Opaque(default) to Fade or Transparent.
A simple function that can do this:
void blink(GameObject obj, float blinkSpeed, float duration)
{
StartCoroutine(_blinkCOR(obj, blinkSpeed, duration));
}
IEnumerator _blinkCOR(GameObject obj, float blinkSpeed, float duration)
{
obj.SetActive(true);
Color defualtColor = obj.GetComponent<MeshRenderer>().material.color;
float counter = 0;
float innerCounter = 0;
bool visible = false;
while (counter < duration)
{
counter += Time.deltaTime;
innerCounter += Time.deltaTime;
//Toggle and reset if innerCounter > blinkSpeed
if (innerCounter > blinkSpeed)
{
visible = !visible;
innerCounter = 0f;
}
if (visible)
{
//Show
show(obj);
}
else
{
//Hide
hide(obj);
}
//Wait for a frame
yield return null;
}
//Done Blinking, Restore default color then Disable the GameObject
obj.GetComponent<MeshRenderer>().material.color = defualtColor;
obj.SetActive(false);
}
void show(GameObject obj)
{
Color currentColor = obj.GetComponent<MeshRenderer>().material.color;
currentColor.a = 1;
obj.GetComponent<MeshRenderer>().material.color = currentColor;
}
void hide(GameObject obj)
{
Color currentColor = obj.GetComponent<MeshRenderer>().material.color;
currentColor.a = 0;
obj.GetComponent<MeshRenderer>().material.color = currentColor;
}
Usage:
void Start()
{
blink(gameObject, 0.2f, 5f);
}
If this is a SpriteRender, you have to replace all the obj.GetComponent<MeshRenderer>().material.color code with obj.GetComponent<SpriteRenderer>().color.

How do I play an animation for twenty-seven seconds or more and load a scene in unity3d

Right now I have the script set to switch scenes when the players health gets low. I want is the die animation to play first than scene load after twenty-eight seconds . I am going to trigger the die animation . The die animation clip is 26.0 . The load scene the going to be gameover scene . The gameover scene needs to load 28.0 or 29.0 . Somewhere around their . Here is my code :
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
[System.Serializable]
[RequireComponent(typeof(SpriteDatabase))]
public class Healthbar : MonoBehaviour {
public int fontSize;
public static int playersHealth;
public int health;
int healthNormalized;
GameObject player;
Image frame;
Image bar;
public int displayCritical;
public int displayRedBar;
public int displayYellowBar;
public string healthMessage;
public string criticalMessage = "Critical";
public string playerTag;
Text Message;
Text Critical;
public bool showHealthValue;
public bool showCritical;
public string sceneToLoad = "T";
SpriteDatabase sd;
public Theme chosenTheme;
public FontNames chosenFont;
int myTheme;
int myFontTheme;
public enum Positioning {
TopLeft,
TopRight,
BottomLeft,
BottomRight
}
[HideInInspector]
public bool alive = true;
//For demo purposes, store player's initial transform (so later it can be respawned there)
Vector3 startPos;
//used to choose between left or right alignment
public Positioning positioning;
//On Start, assign SpriteDatabse class to 'sd'. (Note: That class can never be missing due to the dependency system)
//It then runs Debugger() (find it below.) It checks whether the required sprites are assigned in the inspector, etc.
//Then, it builds hierarchy for GUI (find below)
void Start(){
sd = GetComponent<SpriteDatabase>();
fontSize = Mathf.Clamp(fontSize, 5, 30);
Debugger();
BuildHierarchy();
startPos = player.transform.position;
}
//Converts health integer to float value and updates it every frame.
//Keeps the GUI bar (image) fill amount value synchronized with the health value.
//Note: healthNormalized cuts the number so that it's on a 100 scale like in every game (it's basically the percentage)
void FixedUpdate(){
if (player) {
if (alive) {
/*
if (healthNormalized <= 0) {
alive = false;
die();
}
*/
healthNormalized = health/10;
//Converts health value to a float (range 0-1) so it can be used for image.fillamount
float healthValue = health * 0.001f;
healthValue = Mathf.Clamp(healthValue, 0, 1);
//Checks if it's time to turn the bar color to red or yellow (replace the sprite basically)
CheckForBarColor();
bar.fillAmount = healthValue;
}
DisplayText();
}
else
player = GameObject.FindGameObjectWithTag("Player");
}
void DisplayText(){
if (showHealthValue)
Message.text = healthMessage + ": " + healthNormalized.ToString();
if (healthNormalized <= displayCritical && alive && showCritical) {
Critical.enabled = true;
}
else
Critical.enabled = false;
}
//Called by every object affecting player's health.
//Class that calls it: ApplyDamage
//See that for more info on how to use it!
public void ModifyHealth(int amount) {
if (alive)
health = health - amount;
if (health <= 0) {
Debug.Log("1: sceneToLoad = " + sceneToLoad);
if ((sceneToLoad != "") && (SceneManager.GetSceneByName(sceneToLoad) != null)) {
Debug.Log("2: sceneToLoad = " + sceneToLoad);
SceneManager.LoadScene(sceneToLoad);
}
}
else {
health = Mathf.Clamp(health, 0, 1000);
}
}
}
Put SceneManager.LoadScene(sceneToLoad); in another function then call that function with Invoke("myfunction",25);. It will wait 25 seconds then call myfunction which will then load your scene by calling SceneManager.LoadScene(sceneToLoad);.
You can also start a coroutine and wait with yield return new WaitForSeconds(25f); then execute SceneManager.LoadScene.
As for your code, replace the ModifyHealth function with the function below:
public void ModifyHealth(int amount)
{
if (alive)
health = health - amount;
if (health <= 0)
{
Debug.Log("1: sceneToLoad = " + sceneToLoad);
if ((sceneToLoad != "") && (SceneManager.GetSceneByName(sceneToLoad) != null))
{
Debug.Log("2: sceneToLoad = " + sceneToLoad);
//Play your animation
//Call loadNewScene after 25 seconds
Invoke("loadNewScene",25);
}
}
else
{
health = Mathf.Clamp(health, 0, 1000);
}
}
void loadNewScene()
{
SceneManager.LoadScene(sceneToLoad);
}

Categories

Resources