How do I you images as a score into my game? - unity3d

I have been trying to make a game that's a parody off of minesweeper in unity. I have gotten most of the stuff done including the timer. One problem I have been having is the score count. The score count consists of 3 images. What I am looking is a way so that I can change each image based on how many mines have been caught. Here's the code for it:
public int scorecount = 0;
int i = 0;
int a;
public int amountOfMines;
public Image faceImage;
public Sprite sprite;
private Timer timer;
public ItemDrag itemDrag;
public List<Image> scoreCountImages;
public Sprite[] digitSprites;
void Start()
{
timer = GetComponent<Timer>();
}
void Update()
{
if(scorecount >= i)
{
if(scorecount == amountOfMines) //If the score equals the amount of mines, stop timer, change face sprite.
{
Debug.Log("All the Mines have been cleared");
faceImage.sprite = sprite;
timer.isStop = true;
itemDrag.thereAreStillMines = false;
}
}
}

Your question has a simple solution. Easily use multiple if, In the following code, there is a Sprite coordinator with points that you can match the desired Sprites with the desired conditions.
public void SyncSprite()
{
if (score >= amountOfMines) faceImage.sprite = faceSprites[2]; // total mines
else if (score >= 7) faceImage.sprite = faceSprites[1]; // for e.g after 7 score change to second sprite
else if (score >= 0) faceImage.sprite = faceSprites[0]; // default sprite
}
For Example, at the bottom, then click on a box and there is no mine there. The score goes higher and the sync code is executed at the end of the score calculation and syncs the sprite with the score.
public void OnClickedBox()
{
// do something...
if (noMineWasHere) score++;
SyncSprite();
}

TLDR; Your mistake was rendering 3-digits as a whole thing; You should have 3-sprite renderer, each displaying a single digit.
Set in the inspector, an array of Sprites for the digits, ordered properly.You will use the array as a Dictionary to easily access any digit-sprite by a corresponding number:
[SerializeField, Tooltip("Your number 0-9 sprites should be here, ordered correctly.")]
private Sprite[] numberSprites;
// ......
numberSprites[4] // will output '4' sprite;
Next, rather than have 1 big whole renderer to represent all 3 digits, we can split it into 3 renderer, and expose it to the inspector as an array:
[SerializeField]
private SpriteRenderer[] scoreSpriteRenderers;
Each of this renderer should only render 1 single-digit, something like this:
Now we can easily set-up a loop, to loop through each number in the score, and change the sprite based on the number.
The full result:
[SerializeField, ToolTip("Your number 0-9 sprites should be here, ordered correctly.")]
private Sprite[] numberSprites;
[SerializeField]
private SpriteRenderer[] scoreSpriteRenderers;
private int scoreCount;
// Converts the '123' into a {1, 2, 3}
// See: https://stackoverflow.com/questions/829174/is-there-an-easy-way-to-turn-an-int-into-an-array-of-ints-of-each-digit
public int[] NumbersIn(int value) {
var numbers = new Stack<int>();
for(; value > 0; value /= 10)
numbers.Push(value % 10);
return numbers.ToArray();
}
// Call this whenever score is updated
private void UpdateScoreUI() {
int[] arrScore = NumbersIn(scoreCount);
for (int i = 2; i > 0; --i) {
// If the number was say, 11, the list will only be { 1, 1 }
// So we will need to handle the last digit that is out-of-index.
if (arrScore.Length - 1 <= i){
int currNumber = arrScore[i];
scoreSpriteRenderers[i].sprite = scoreSpriteRenderers[currNumber];
} else {
scoreSpriteRenderers[i].sprite = scoreSpriteRenderers[0];
}
}
}
Edit
Yes, this method works with Image objects as well, since they have a sprite property, and we are only interacting with that property.

Related

Destroy gameObject in random range

Look i dont have error but its not work to me
you know why??
enter image description here
You could use (int)Mathf.Round(Random.Range(1, 5)) which you could then check if it was equal to any number. Any number will have about a 1 in 5 chance.
You could use this script to detect clicks:
public float randomness = 2f;
void Update()
{
if (Input.GetKeyDown(KeyCode.Mouse0))
{
if ((int)Mathf.Round(Random.Range(0f, randomness)) == 0)
{
//Destroy the object
//This will have a 1/2 chance
}
}
}

How to update sprite position faster?

For the game I'm trying to do, I have quite a few objects:
Where all start with a specific sprite, like the Hero:
Except that the Hero is the only one which has an Animator component and follows animation states.
The other game objects would get their sprite and position based on the Hero, using the following script:
using UnityEngine;
public class SpritePosition : MonoBehaviour {
[SerializeField] private string objectName;
[SerializeField] private int objectIndex;
[SerializeField] private int objectR;
[SerializeField] private int objectG;
[SerializeField] private int objectB;
private Rigidbody2D body;
private SpriteRenderer objectRenderer;
private GameObject hero;
private Rigidbody2D heroRigidBody;
private SpriteRenderer heroRenderer;
private Sprite currentHeroSprite;
private HeroResources heroResourcesScript;
private HeroMovement heroMovementScript;
private Sprite[] spriteGroup;
private void Start() {
body = GetComponent<Rigidbody2D>();
objectRenderer = GetComponent<SpriteRenderer>();
hero = GameObject.Find("Hero");
heroRigidBody = hero.GetComponent<Rigidbody2D>();
heroRenderer = hero.GetComponent<SpriteRenderer>();
currentHeroSprite = heroRenderer.sprite;
heroResourcesScript = hero.GetComponent<HeroResources>();
Debug.Log(objectName + "(" + objectR + ", " + objectG + ", " + objectB + ")");
spriteGroup = heroResourcesScript.spriteGroup[objectName];
}
private void Update() {
heroMovementScript = hero.GetComponent<HeroMovement>();
if (currentHeroSprite != heroRenderer.sprite) {
currentHeroSprite = heroRenderer.sprite;
}
SetSprite();
SetPosition();
}
private bool shouldMirrorSprite(int index) {
return index >= 0 && index <= 34 ||
index >= 69 && index <= 71 ||
index >= 81 && index <= 83 ||
index >= 97 && index <= 99 ||
index >= 117 && index <= 118 ||
index >= 133 && index <= 175;
}
private void SetSprite() {
int currentSpriteIndex = int.Parse(currentHeroSprite.name.Replace("hero-body_", ""));
objectRenderer.sprite = spriteGroup[currentSpriteIndex];
objectRenderer.color = new Color32((byte)objectR, (byte)objectG, (byte)objectB, 255);
if (heroMovementScript.isFacingLeft && shouldMirrorSprite(currentSpriteIndex)) {
transform.localScale = new Vector3(-1, 1, 1);
} else {
transform.localScale = Vector3.one;
}
}
// for this to work, the game object must have a
// RigidBody2D component with Freeze Position active
// for X and Y axis
private void SetPosition() {
Vector2 currentHeroPosition = heroRigidBody.position;
transform.position = currentHeroPosition;
}
}
Which, when added as a component, expects a name, index, and R, G, and B values:
Here, the Name is necessary to load specific sprites for each game object, in this script:
using System.Collections.Generic;
using UnityEngine;
public class HeroResources : MonoBehaviour
{
public Dictionary<string, Sprite[]> spriteGroup = new Dictionary<string, Sprite[]>();
void Awake () {
spriteGroup.Add("pants", Resources.LoadAll<Sprite>("Spritesheets/pants"));
spriteGroup.Add("boots", Resources.LoadAll<Sprite>("Spritesheets/boots"));
spriteGroup.Add("shirt", Resources.LoadAll<Sprite>("Spritesheets/shirt"));
spriteGroup.Add("tunic", Resources.LoadAll<Sprite>("Spritesheets/tunic"));
spriteGroup.Add("belt", Resources.LoadAll<Sprite>("Spritesheets/belt"));
Debug.Log(spriteGroup.Count);
}
}
and the sprites are loaded from several folders in the Resources folder:
These spritesheets are all of the same size, so they can be cleanly sliced:
Thus, having the sprites like this, I can simply call the SetSprite function and the SetPosition function based on the Hero:
private void SetSprite() {
int currentSpriteIndex = int.Parse(currentHeroSprite.name.Replace("hero-body_", ""));
objectRenderer.sprite = spriteGroup[currentSpriteIndex];
objectRenderer.color = new Color32((byte)objectR, (byte)objectG, (byte)objectB, 255);
if (heroMovementScript.isFacingLeft && shouldMirrorSprite(currentSpriteIndex)) {
transform.localScale = new Vector3(-1, 1, 1);
} else {
transform.localScale = Vector3.one;
}
}
// for this to work, the game object must have a
// RigidBody2D component with Freeze Position active
// for X and Y axis
private void SetPosition() {
Vector2 currentHeroPosition = heroRigidBody.position;
transform.position = currentHeroPosition;
}
This works, and the other sprites update based on the index of the Hero game object and follow it, so it looks like the user has a lot of equipment. However, sometimes the sprites either fail to keep up or the position falls behind a bit:
There also seems to be some black lines toward the top of the sprites. I assume this happens because the sprites do not load fast enough, and if so, is there a way to ensure these sprites load faster? Or does this have to do with a computer's performance?
Also, to have the sprites follow, I need to freeze X and Y position on the other game objects' RigidBody2D. The Hero has Transform, Sprite Renderer, Box Collider 2D, RigidBody2D, two scripts (HeroMovement and HeroResources), and Animator components, while the other game objects only have Transform, Sprite Renderer, RigidBody2D, and a script (SpritePosition)
Instead of positioning the clothes yourself you could let Transform component do the heavy lifting by just placing the cloth GameObjects under the hero GameObject with correct offsets.
This way you could also have HeroAnimator component that gets all the clothing components under the hero with GetComponentsInChildren at startup and changes the sprites and flip them to match movement direction. Clothing components could focus
because the sprites do not load fast enough
You're loading all the sprite resources with HeroResources.Awake so they should all be loaded to memory during startup. Same goes for all sprites you're referencing in your scene.
String operations like this in update method can generate surprising amount of garbage for the garbage collector which can affect performance down the line.
int currentSpriteIndex = int.Parse(currentHeroSprite.name.Replace("hero-body_", ""));
Its good to note that implementing something like this can get really tricky. Many 2D games that let you change outfit use skeletal animation or have very limited set of minimal animations.

Counting number of clicks

I want to use Unity to calculate the number of clicks on the page as well as the number of clicks on a coin that appears every few seconds. Can you tell me how I can get the number of clicks on the page?
each time I want the number of clicks on the coins and the page to be calculated separately.
Its not clear what you have in mind about coin. Its a UI element or a GameObejct.
I suppose its an object with RigidBody and Collider:
public class HitDetector : MonoBehaviour
{
public static int allCounter = 0;
public static int hitCounter = 0;
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
allCounter++;
if (IsHit())
{
hitCounter++;
}
Debug.Log($"All: {allCounter} & hit: {hitCounter}");
}
}
private bool IsHit()
{
return Physics2D.Raycast(Camera.current.ScreenToWorldPoint(Input.mousePosition), Vector2.zero);
}
}

How to switch between two materials assigned to one gameobject in Unity3D?

I've got two materials attached to one gameobject and what I want to do is switch from one material to another after a few seconds have gone by.
In Unity3D under the inspector menu on the Gameobject that I have selected, on the MeshRenderer heading underneath the materials sub-heading I have increased the size from 1 to 2. I've assigned two materials to the two newly created elements. But when I run the scene the materials do not switch.
public var arrayMAterial : Material[];
public var CHILDObject : Transform;
function Update() {
CHILDObject.GetComponent.<Renderer>().material = arrayMAterial[0];
}
There are no error messages. It just does not switch to the new material.
Here's a quick C# Script that will cycle through an array of materials after a delay.
using UnityEngine;
public class SwitchMaterialAfterDelay : MonoBehaviour
{
[Tooltip("Delay in Seconds")]
public float Delay = 3f;
[Tooltip("Array of Materials to cycle through")]
public Material[] Materials;
[Tooltip("Mesh Renderer to target")]
public Renderer TargetRenderer;
// use to cycle through our Materials
private int _currentIndex = 0;
// keeps track of time between material changes
private float _elapsedTime= 0;
// Start is called before the first frame update
void Start()
{
// optional: update the renderer with the first material in the array
TargetRenderer.material = Materials[_currentIndex];
}
// Update is called once per frame
void Update()
{
_elapsedTime += Time.deltaTime;
// Proceed only if the elapsed time is superior to the delay
if (_elapsedTime <= Delay) return;
// Reset elapsed time
_elapsedTime = 0;
// Increment the array position index
_currentIndex++;
// If the index is superior to the number of materials, reset to 0
if (_currentIndex >= Materials.Length) _currentIndex = 0;
TargetRenderer.material = Materials[_currentIndex];
}
}
Be sure to assign the materials and renderer to the component, otherwise you will get errors!
hth.

Finding gameObject via direction of input

I have a list of gameObjects added to a List within the area as shown. What I need is a directional input to choose a target from the origin point. I have got it work to get this origin point.
My first attempt of this was to get it via rayCast, but by doing that there were times directional inputs need to directly hit target object by the ray. This is no problem if input was done like case #1.
However, what I need it to really work is when input direction was as if case #2 or #3 the input will still get a target. My second attempt was to do this with sphereCast, but it still needed a target in sphere proximity and multiple targets hit by sphereCast still needed to result in only one and more accurate target selection by input.
Since I have all the transform.position of all the possible targets as well as the origin point I wondered there would be a more elegant way of resolving this via comparing vector3's of these coordinates(origin and targets in the general direction).
Here's my latest approach:
//
// m_targetList is the list containing all GameObjects as GameObjects in other script m_collector. All m_collector does is this.
//
using System.Collections.Generic;
using UnityEngine;
public class TargetSwitcher : MonoBehaviour
{
private TargetCollector m_collector;
private Transform m_origin;
public bool m_targetChanged = false;
public GameObject m_target;
public LayerMask m_targetMask;
private Dictionary<Vector3, float> m_targetInfo = new Dictionary<Vector3, float>();
private void Awake()
{
m_collector = GetComponent<TargetCollector>();
m_origin = GameObject.Find("TargetOrigin").transform;
m_tracker = GameObject.Find("TargetTracker").transform;
m_bound = GetComponent<BoxCollider>();
}
public void UpdateBearing(GameObject origin)
{
m_origin = origin.transform;
foreach (GameObject target in m_collector.m_targetList)
{
Vector3 dir = (target.transform.position - origin.transform.position).normalized
float dist = Vector3.Distance(origin.transform.position, target.transform.position);
m_targetInfo.Add(dir, dist);
}
}
public void SwitchTarget()
{
if (!m_targetChanged)
{
Vector2 dir = new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical")).normalized;
// Find closest direction value from Dictionary to dir of here
// Compare distance from origin if multiple targets and choose the nearest
}
}
public void ReturnToIdle()
{
m_origin.position = m_target.transform.position;
m_targetChanged = false;
m_targetInfo.Clear();
}
public struct TargetInfo
{
public Vector3 bearing;
public float distance;
public TargetInfo(Vector3 bearing, float distance)
{
this.bearing = bearing;
this.distance = distance;
}
}
}
Generally, I'm trying to compare the normalized vector of directional input to the normalized vector from the origin to each target before SwitchTarget(). The input method here is Gamepad axis x and y as Horizontal and Vertical.
Reposting this question since a provided answer was very far from the question and marked as duplicate(Given answer was about finding gameObject by distance only, this question is about direction and distance portion is to compare second-handedly when multiple items were found in the direction)
Edit
After some trials with dot product now I'm sure this is much likely where I want to head. There are much inconsistency I need to get on with, though.
Here's my most recent attempt:
private void Update()
{
UpdateBearing();
Vector3 input = new Vector3(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"), 0);
if (input != Vector3.zero)
{
SwitchTarget();
}
}
public void UpdateBearing(GameObject origin)
{
m_origin.position = origin.transform.position;
foreach (GameObject target in m_collector.m_targetList)
{
Vector3 dir = (target.transform.position - origin.transform.position).normalized;
if (!m_targetInfo.ContainsKey(target))
{
m_targetInfo.Add(target, dir);
}
}
}
public void SwitchTarget()
{
GameObject oldTarget = m_collector.m_target;
if (!m_targetChanged)
{
Vector3 dir = new Vector3(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"), 0).normalized;
Debug.DrawRay(m_origin.position, dir * 100, Color.yellow, 0.5f);
foreach (KeyValuePair<GameObject, Vector3> possibleTarget in m_targetInfo)
{
float dot = Vector3.Dot(dir, possibleTarget.Value);
if (dot > 0.5f) // Compare DP difference of added dot and input dot
{
GameObject newTarget = possibleTarget.Key;
if (oldTarget != newTarget)
{
Debug.Log(possibleTarget.Value + " // " + dot);
m_target = newTarget;
m_collector.m_target = newTarget;
m_targetChanged = true;
}
}
}
}
}
With this, I'm kind of getting gameObject selection without raycasting and missing any targets. However, I'm sure I need better case comparison than if(dot > 0.5f). Also, my rough assumption is that if I don't update the value of the dictionary m_targetInfo for each Key I'd have another inconsistency if those targets ever move. Anyways, I'm still confused how properly utilize this to achieve my end goal.
Since you have all the desired game objects in the area you can create a for loop and check the angle between your look direction and their position, if it is lower than some value (you can make it super low so it's precise or a little bit higher to allow for some margin of error) put it in a list of gameobjects and if there's more than one object there get the closest one.
The code for getting closest object in angle would look something like this:
GameObject CheckObjects()
{
List<GameObject> InAngle = new List<GameObject>();
for(int i = 0; i < YourObjectsList.Count; i++)
{
GameObject tested = YourObjectsList[i];
Vector3 dir = tested.transform.position - origin.transform.forward;
// I'm assuming here that youre rotating your origin with the
directional input, not then instead of origin.transform.forward
place there your directional vector3
float angle = Vector3.Angle(dir, tested.transform.position);
if(angle <= desiredAngle)
{
InAngle.Add(tested);
}
}
GameObject closest = null;
for(int j = 0; j < InAngle.Count; i++)
{
GameObject tested = InAngle[i];
Vector3 dir1 = tested.transform.position - origin.transform.position;
Vector3 dir2 = closest.transform.position - origin.transform.position;
if(!closest)
{
closest = tested;
}
else
{
if(dir2.sqrMagnitude > dir1.sqrMagnitude)
{
closest = tested;
}
}
}
return closest;
}