On my scene view in unity, I have three gameobjects set up, each with a respective sprite renderer and sprites. Two of the three are smaller in dimensions than the third one.
The images displayed by both smaller sprites are different. What I'd like to do is to be able to change the sprite of the third one by clicking on the other two. Clicking on one of the smaller sprites will make the third larger sprite match the small sprite that was previously clicked.
Here are the relevant scripts assigned to the two smaller sprites.
1st one that handles which sprite is displayed on the small sprite:
public class CardModel : MonoBehaviour
{
SpriteRenderer spriteRenderer; // renders sprite
public int cardIndex; // indicates what the card is
public bool show; // shows or hides card
public Sprite[] cardFronts; // collection of cards' front sides
public Sprite cardBack; // backside of all cards
// if card is turned up, show front of card, show back otherwise
public void DisplaySprite()
{
if (show)
spriteRenderer.sprite = cardFronts[cardIndex];
else
spriteRenderer.sprite = cardBack;
}
void Awake()
{
spriteRenderer = GetComponent<SpriteRenderer>(); // retrieve our sprite renderer
DisplaySprite(); // show card's sprite
}
}
And 2nd one that makes the smaller sprites clickable:
public class ClickCard : MonoBehaviour
{
CardModel model; // used to retrieve card index and bool to assign to larger sprite
public GameObject largeSprite; // third large sprite
void OnMouseDown()
{
CardModel cModel = GetComponent<CardModel>(); // retrieve card model from smaller sprite
LargeCardModel largeCardModel = largeSprite.GetComponent<LargeCardModel>(); // retrieve card model for larger sprite
largeCardModel.matchSprite(cModel.show, cModel.cardIndex); // match large sprite with small sprite clicked
}
}
And finally, the script attached to the third large sprite:
public class LargeCardModel : MonoBehaviour
{
public int largeIndex; // determines which sprite to show
public Sprite[] largeCardFaces; // collection of large sprites
public Sprite largeCardBack; // large card back
SpriteRenderer largeRenderer; // renders the image upon the large sprite
void Awake()
{
largeRenderer = GetComponent<SpriteRenderer>(); // retrieve renderer
}
// method to allow sprite to be changed
public void matchSprite(bool showFace, int cIndex)
{
if (largeRenderer == null)
largeRenderer = GetComponent<SpriteRenderer>(); // for some reason, I need to include this line or else I get an exception
if (showFace)
largeRenderer.sprite = largeCardFaces[cIndex];
else
largeRenderer.sprite = largeCardBack;
}
}
For whatever reason, I can't get the image to the larger sprite to change during runtime however when I stop running the program, the large sprite changes to that of the last smaller sprite I clicked on.
Okay, bare with as I have not tested this in Unity yet.
I think you're over complicating what you are trying to do. What you want is basically when sprite 1 or 2 is clicked, sprite 3 becomes what you have just clicked.
Here's an idea to try out:
Small Card GameObject:
public int cardIndex; // indicates what the card is
public bool show; // shows or hides card
public Sprite[] cardFronts; // collection of cards' front sides
public Sprite cardBack; // backside of all cards
GameObject largeSprite; //the large sprite that we will be changing!
void Start()
{
largeSprite = GameObject.Find("Name of the large sprite"); //find our large sprite!
}
// if card is turned up, show front of card, show back otherwise
public void DisplaySprite()
{
if (show)
gameObject.sprite = cardFronts[cardIndex]; //or it could be gameObject.renderer.sprite, I havent tested!
else
gameObject.sprite = cardBack;
}
void Awake()
{
DisplaySprite(); // show card's sprite
}
void OnMouseDown()
{
largeSprite.sprite = gameObject.sprite; //make the large one have the same as the small one!
}
Explanation:
Assuming this works, I've completely removed the script from the large GameObject and merged the 2 scripts together in the small one.
In Start() we find the large card gameObject using GameObject.Find() that way Unity will find your object with the name you've supplied and will go from there.
In OnMouseDown() we basically say:
if you clicked on me
make the large card the same as me! I havent tested it so that line might be a bit off, but the idea is there.
All in all, this is now one script that is attached to your small card object. Like I said I havent tested it, but I think the idea is there.
Hope this helps! :)
Related
I am trying to do when i destroy all boxes something happen.
My code is;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class destroy : MonoBehaviour
{
private string BALL_TAG = "ball";
public AudioClip coin;
public AudioSource src;
public float numBox = 120f;
public bool isDestroyed;
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.CompareTag(BALL_TAG))
{
src.clip = coin;
src.Play();
Destroy(gameObject);
isDestroyed = true;
}
}
private void Update()
{
boxes();
}
public void boxes()
{
if(isDestroyed == true)
numBox -= 1f;
if(numBox == 119)
SceneManager.LoadScene("mainManu");
}
private IEnumerator Two()
{
yield return new WaitForSeconds(1f);
Destroy(gameObject);
}
}
But it doesn't work.
It is suppose to do when I broke 1 box it sends me to menu.
I think its problem in "numBox -= 1f;" because I don't know hot to make this.
I don't understand your code completely. So, I need to make some assumptions.
I think the Script is attached to the box and every box has this Script. I also think, that your player Shoots Ball. Those Balls have a collider with an ball tag.
There are multiple problems with your code.
The first one is, that your count variable, numBox, is saved in your destroy Script, which is placed on each box.
this means, that every Box is counting for itself.
You have to centralize this. There are multiple ways for doing this.
One way is to declare this variable as static(https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/static)
This is not best practice, but works.
A Better way is to have a Script on Your Player, which holds this number and every Box searches for this Script and change this number if it is destroyed.
The second big Problem is, that your are doing some really weird thing in your Update and the collision handling
First of all, you are setting isDestroyed to true. Then in your boxes method, which is called in every Frame, you are decrementing your numBox variable by one, if this is Destroyed is true.
So if your Box gets hit, you are decrementing every frame.
After that you are checking every frame if your numBox is 119
If so, you change the Scene.
This is the reason, why you are getting to your MainMenu after only one boy
This behaviour is very weird, because it is totally unnecessary. You can reduce your variable directly in in your OnCollisionEnter2D Method.
There are some little things, which can be improved.
When you are trying to play a Sound, you don't have to specify the AudioClip in code. You can assign this directly in Unity on the AudioSource Component via drag and drop. This makes your code simpler.
You are not calling the Two Coroutine. You've specified this Coroutine but don't call it.
//Script on Player
public class PlayerBoxDestroyManager:MonoBehaviour
{
public int StartBoxes = 120;
private int Boxes;
private void Start()
{
Boxes = StartBoxes;
}
public void DestroyBox()
{
//Reduce our Boxes count
//This is equal to Boxes -= 1
// Boxes = Boxes -1
Boxes--;
// If we have less or zero Boxes left, we End call our EndGame methode
if(Boxes <= 0)
{
EndGame();
}
}
private void EndGame()
{
// We change the Scene to the mainMenu
SceneManager.LoadScene("mainManu");
}
}
```
//Script on all Boxes
public class Box : MonoBehaviour
{
public string Balltag = "ball";
//Audio Source the Audio Clip has to be assigned in the Unity editor
public AudioSource Coin;
private void OnCollisionEnter2D(Collision2D collision)
{
//Check it colliding Object has the right Tag
if(collision.transform.tag == Balltag)
{
//Get the reference to the Player Script
PlayerBoxDestroyManager PBDM = FindObjectOfType<PlayerBoxDestroyManager>();
//We can now access the Destroy Box Methode
PBDM.DestroyBox();
//Play the sound
Coin.Play();
//If we destroy our Object now, the Sound would also be deletet.
//We want to hear the sound, so we have to wait, till the sound is finished.
StartCoroutine(WaitTillAudioIsFinished());
}
}
IEnumerator WaitTillAudioIsFinished()
{
//we wait till the sound is finished
while (Coin.isPlaying)
{
yield return null;
}
//if finished, we destroy the Gameobject
Destroy(gameObject);
}
}
I hope I helped you. If you have questions, feel free to ask.
And sorry for my English:)
I have a game wehre you are a cube and you dodge obstacles, I just implemented the ability to change the color of the cube like changing skin. I did that by assigning a different material to the player when he presses "2".
Here is the script:
void Update()
{
if (Input.GetKeyDown("2"))
{
Object.GetComponent<MeshRenderer>().material = Material1;
}
}
When you die the scene resets and when you win a new scene is loaded, I would like the game to remember the material change even after the scene is reset or a new scene is loaded. I have done some research and found something called "PlayerPrefs" and I have been playing around with it but nothing even got close to working and I didn't really understand what i was doing.
I really want to understand how this works becuase I know i will be using it alot when making games. Can someone help me understand?
Thanks.
Create one gameobject and apply this script
public class SavingMaterial : MonoBehaviour
{
public static SavingMaterial instance;
public Material mat;
void Awake()
{
if(instance == null)
{
instance = this;
DontDestroyOnLoad(base.gameObject);
}
else
{
Destroy(base.gameObject);
}
}
public void StoreMaterial(MeshRenderer mesh)
{
mat = mesh.material;
}
}
When you want to store material call this function like this
SavingMaterial.instance.StoreMaterial(you meshrenderer component);
and when you need this material just get from this class like this
material = SavingMaterial.instance.mat;
and note that if you quit the game you loose saved material as it is store in variable otherwise scene change and reset won't affect it.
you only save int,string and bool in playerprefs. you have to store values of materials into string and then save this string into playerprefs.
alternatively you can assign this material to local variable and set that script on Dontdestroyonload so it will not reset as scene is destroy or reset.
I am following the 2D Roguelike tutorial from unity learn.
When testing my game I enter the play mode and everything is fine, when using the arrows to move the player, unity stops and I can't click at any of the buttons in the editor, and the animation stops.
Here is my player script :
//Delay time in seconds to restart level.
public float restartLevelDelay = 1f;
//Number of points to add to player food points when picking up a food object.
public int pointsPerFood = 10;
//Number of points to add to player food points whne picking up a soda object.
public int pointsPerSoda = 20;
//How much damage a player does to a wall whne chopping it.
public int wallDamage = 1;
//Used to store a refrence to the Player's animator component
private Animator animator;
//Used to store player food points total during level.
private int food;
//Start overrides the Start function of MovingObject
protected override void Start()
{
//Get a component reference to the Player's animator component
animator = GetComponent<Animator>();
//Get the current food point total stored in GameManager.instance between levels.
food = GameplayManager.instance.playerFoodPoints;
//Call the Start function of the MovingObject base class.
base.Start();
}
//This function is called when the behaviour becomes disabled or inactive.
private void OnDisable()
{
//When Player object is disabled, store the current local food total in the GameManager so it can be re-loaded in next level.
GameplayManager.instance.playerFoodPoints = food;
}
private void Update()
{
//If it's not the player's turn, exit the function.
if (!GameplayManager.instance.playersTurn) return;
int horizontal = 0; //Used to store the horizontal move direction.
int vertical = 0; //Used to store the vertical move direction.
//Get input from the input manager, round it to an integer and store in horizontal to set x axis move direction
int v = (int) (Input.GetAxisRaw("Horizontal"));
horizontal = v;
//Get input from the input manager, round it to an integer and store in vertical to set y axis move direction
vertical = (int) Input.GetAxisRaw ("Vertical");
//Check if moving horizontally, if so set vertical to zero.
if (horizontal != 0)
{
vertical = 0;
}
//Check if we have a non-zero value for horizontal or vertical
if (horizontal != 0 || vertical != 0)
{
//Call AttemptMove passing in the generic parameter Wall, since that is what Player may interact with if they encounter one (by attacking it)
//Pass in horizontal and vertical as parameters to specify the direction to move Player in.
AttemptMove<Wall>(horizontal, vertical);
}
}
//AttemptMove overrides the AttemptMove function in the base class MovingObject
//AttemptMove takes a generic parameter T which for Player will be of the type Wall, it also takes integers for x and y direction to move in.
protected override void AttemptMove<T>(int xDir, int yDir)
{
//Every time player moves, subtract from food points total.
food--;
//Call the AttemptMove method of the base class, passing in the component T (in this case Wall) and x and y direction to move.
base.AttemptMove<T>(xDir, yDir);
//Hit allows us to reference the result of the Linecast done in Move.
RaycastHit2D hit;
//If Move returns true, meaning Player was able to move into an empty space.
if (Move(xDir, yDir, out hit))
{
//Call RandomizeSfx of SoundManager to play the move sound, passing in two audio clips to choose from.
}
//Since the player has moved and lost food points, check if the game has ended.
CheckIfGameOver();
//Set the playersTurn boolean of GameManager to false now that players turn is over.
GameplayManager.instance.playersTurn = false;
}
//OnCantMove overrides the abstract function OnCantMove in MovingObject.
//It takes a generic parameter T which in the case of Player is a Wall which the player can attack and destroy.
protected override void OnCantMove<T>(T component)
{
//Set hitWall to equal the component passed in as a parameter.
Wall hitWall = component as Wall;
//Call the DamageWall function of the Wall we are hitting.
hitWall.DamageWall(wallDamage);
//Set the attack trigger of the player's animation controller in order to play the player's attack animation.
animator.SetTrigger("playerChop");
}
//OnTriggerEnter2D is sent when another object enters a trigger collider attached to this object (2D physics only).
private void OnTriggerEnter2D(Collider2D other)
{
//Check if the tag of the trigger collided with is Exit.
if (other.tag == "Exit")
{
//Invoke the Restart function to start the next level with a delay of restartLevelDelay (default 1 second).
Invoke("Restart", restartLevelDelay);
//Disable the player object since level is over.
enabled = false;
}
//Check if the tag of the trigger collided with is Food.
else if (other.tag == "Food")
{
//Add pointsPerFood to the players current food total.
food += pointsPerFood;
//Disable the food object the player collided with.
other.gameObject.SetActive(false);
}
//Check if the tag of the trigger collided with is Soda.
else if (other.tag == "Soda")
{
//Add pointsPerSoda to players food points total
food += pointsPerSoda;
//Disable the soda object the player collided with.
other.gameObject.SetActive(false);
}
}
//Restart reloads the scene when called.
private void Restart()
{
//Load the last scene loaded, in this case Main, the only scene in the game.
SceneManager.LoadScene(0);
}
//LoseFood is called when an enemy attacks the player.
//It takes a parameter loss which specifies how many points to lose.
public void LoseFood(int loss)
{
//Set the trigger for the player animator to transition to the playerHit animation.
animator.SetTrigger("playerHit");
//Subtract lost food points from the players total.
food -= loss;
//Check to see if game has ended.
CheckIfGameOver();
}
//CheckIfGameOver checks if the player is out of food points and if so, ends the game.
private void CheckIfGameOver()
{
//Check if food point total is less than or equal to zero.
if (food <= 0)
{
//Call the GameOver function of GameManager.
GameplayManager.instance.GameOver();
}
}
How can I fix this?
I probably tried everything.
Regards.
This feels like an infinite loop to me, which would probably lock up the game and editor.
The code you posted calls out to other methods not listed here... I would look for a place where you might be recursively calling a method, or maybe where you're meaning to call base.theMethod but instead called this.theMethod.
This is probably a stack overflow where a function is calling himself infinitely.
It can also be a Coroutine from the base class with a infinite loop.
However, we can't say exactly where in the code, because is missing some information like what's inside the parent class (from the comments I suppose is called MovingObject)
Whenever posting a code be sure to send only the necessary piece of code (maybe removing some useless comments) and giving precious information like the inheritance and if is an override function would be useful to give also his base implementation.
I'm trying to create the board game Hex. Player One at the bottom being yellow and Player Two at the top being blue. When Player One clicks a hex it should become yellow and when Player two clicks a hex it should become blue.
I've created this Hex Map using a prefab and now I want to be able to change the color of each tile when I click on it (The yellow hexes you see will be transparent but the sprite I imported is yellow which is why the color on the Sprite Renderer is white even though the hexes look yellow).
Btw, as of right now changing the color in the Sprite Renderer changes the color of all the hexes.
I followed quill18creates's tutorial to make the Hex Map except I did it in 2D instead of 3D.
https://www.youtube.com/watch?v=j-rCuN7uMR8
As of write now my Color Change script isn't working at all. I was trying to have it so when it receives one click it changes to yellow. Then the next click to blue, the next to yellow, and so on. Since each player only get's one click.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ColorChange : MonoBehaviour {
public Color[]colors; // allows input of material colors in a set sized array
public SpriteRenderer rend; // what are we rendering? the hex
private int index = 1; //initialize at 1, otherwise you have to press the ball twice to change color
// Use this for initialization
void Start () {
rend = GetComponent<SpriteRenderer> (); // gives functionality for the renderer
}
// Update is called once per frame
void onMouseDown () {
// if there are no colors present nothing happens
if (colors.Length == 0){
return;
}
if (Input.GetMouseButtonDown(0)){
index += 1; // when mouse is pressed down we increment up to the next index location
// when it reaches the end of the colors it stars over
if (index == colors.Length +1){
index = 1;
}
print (index); // used for debugging
rend.color = colors [index - 1]; // this sets the material color values inside the index
}
} //onMouseDown
}
How should I go about implementing this? Any help would be much appreciated!
First things first, you need to properly capitalize OnMouseDown() for it to get called.
After that, since you're using a SpriteRenderer, you'll need to add a collider to detect mouse click events. As mentioned in the OnMouseDown() documentation:
This event is sent to all scripts of the Collider or GUIElement.
Since you have neither, on your prefab, click Add Component > Polygon Collider 2D and it'll automatically create the proper geometry for your sprite (assuming that everything outside the hex is transparent).
Finally, remove your check against Input.GetMouseButtonDown(0). OnMouseDown is already capturing the fact that the mouse was clicked and the specific instance running OnMouseDown() was the instance that was clicked.
add collider and rigid body to your prefab
then capital O in OnMouseDown and delete "if (Input.GetMouseButtonDown(0)){"
Input.GetMouseButtonDown(0) return true when player click anywhere which is not what you want
public Color[] colors; // allows input of material colors in a set sized array
public SpriteRenderer rend; // what are we rendering? the hex
private int index = 1; //initialize at 1, otherwise you have to press the ball twice to change color
// Use this for initialization
void Start()
{
rend = GetComponent<SpriteRenderer>(); // gives functionality for the renderer
}
// Update is called once per frame
void OnMouseDown()
{
// if there are no colors present nothing happens
if (colors.Length == 0)
{
return;
}
index += 1; // when mouse is pressed down we increment up to the next index location
// when it reaches the end of the colors it stars over
if (index == colors.Length + 1)
{
index = 1;
}
print(index); // used for debugging
rend.color = colors[index - 1]; // this sets the material color values inside the index
} //onMouseDown
using UnityEngine;
public class PlayerLoad : MonoBehaviour
{
[SerializeField]
private Sprite pSprite;
private void Start()
{
LoadSprite(this.gameObject, pSprite);
}
void LoadSprite(GameObject p1, Sprite pSprite = null) // p1 = the player's gameobject
{
var sr = p1.GetComponent<SpriteRenderer>();
if (sr == null)// If no sprite renderer exist
{
sr = p1.AddComponent<SpriteRenderer>();
}
if (sr != null && !sr.enabled)// If sprite renderer exist but isn't active
{
sr.enabled = true;
}
if (sr.sprite == null)// If no sprite exist, adds one
{
p1.GetComponent<SpriteRenderer>().sprite = pSprite;
}
}
}
Ok, so I'm having an issue to where sometimes my player's sprite seems to be invisible. As of now, I can build the project onto my mobile device and everything works fine. However, when the second level is completed (Right now I only have two levels I'm using for testing) the game goes to the Death scene. Then it ask's the user to continue or quit. If continue, the player is taken to the last level reached. Code works, but the sprite is now invisible. Sometimes I can pause the game, quit and return to the main menu, click play again and start over and the player appears again. Other times it makes it worse because the bullets even fail to render. I have no clue as to what could cause such a thing. SO I have this code in hopes of forcing the sprite to render whether it wants to or not.
Here's a screenshot of the mobile screen:
To the right you can see the bullets firing but the player cannot be seen. You can tell the player is moving by the offset in the bullet trajectory. (If you look close)
I'm using Unity 2019.0.1a Beta