Unity how to scale gameobjects using mouseclick? - c#

does anyone know how to scale up different game objects using mouse click and after doing the scaling, the game object should not be able to scale up again. Currently my code is able to scale up but its not what i wanted.
Heres my current code:
public void ScaleForRuler()
{
transform.localScale += new Vector3 (3.0F, 0, 0.1F);
}
void OnMouseDown()
{
if (touch == false)
{
if(ruler.GetComponent<Collider>().name == "Ruler")
{
ScaleForRuler ();
touch = true;
Debug.Log (touch);
}
if(TriangleThingy.GetComponent<Collider>().name == "Triangle_Set_Square_Ruler")
{
ScaleForTriangleThingy ();
touch = true;
Debug.Log (touch);
}
if (Tsquare.GetComponent<Collider> ().name == "Tsquare")
{
if (LineR.GetComponent<Collider> ().name == "LineRight" || LineL.GetComponent<Collider> ().name == "LineLeft")
{
TsquareScaleForTB ();
touch = true;
Debug.Log (touch);
}
else if (LineB.GetComponent<Collider> ().name == "LineBottom" || LineT.GetComponent<Collider> ().name == "LineTop")
{
TsquareScaleForTB ();
touch = true;
Debug.Log (touch);
}
}
if (protractor.GetComponent<Collider> ().name == "Protractor")
{
ScaleForProtractor ();
touch = true;
Debug.Log (touch);
Debug.Log ("protractor scale");
}
}
}

Stop using OnMouseDown(), Unity has introduced since a while some new handler interfaces like IPointerDownHandler, IDragHandler, etc. within the EventSystem class, which works perfectly with mouse AND touch inputs.
It's not really clear what you want to achieve, if you want to scale every single object separately when it's clicked or just scale all together regardless which one is clicked.
I'll assume you want to be able to scale all objects separately. The steps to do that are the following:
1) Add the Physics 2D Raycaster component to your scene camera.
2) To every single object you want to scale, add this simple script:
using UnityEngine;
using UnityEngine.EventSystems;
public class ScaleExample : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler {
private Vector2 startPosition;
private float scalingFactor = .01f;
private bool isObjectAlreadyScaled = false;
public void OnBeginDrag(PointerEventData eventData) {
startPosition = Vector2.zero;
}
public void OnDrag(PointerEventData eventData) {
if (!isObjectAlreadyScaled) {
startPosition += eventData.delta;
var scalingAmount = new Vector2(Mathf.Abs(startPosition.x), Mathf.Abs(startPosition.y));
transform.localScale = (Vector3)scalingAmount * scalingFactor;
}
}
public void OnEndDrag(PointerEventData eventData) {
isObjectAlreadyScaled = true;
}
}
The example works with 2D objects (i.e.: scales up the X and Y of the object), you can ofc change this very easily in order to accomodate different coordinates scaling.
The use of the interfaces should be clear by example: OnBeginDrag is called once as soon as the dragging starts, OnDrag is called repeatedly every time there's a change in the position of the pointer during the drag, and OnEndDrag is called as soon as the drag is over (i.e.: user releases the button/finger).

In a comment to your question, you wrote
So i wanted to ask if i could just use a single script to control scaling for many diff objects
The answer is yes (and it is a really good idea also)
Here is an idea on how to do it:
From the doc of OnMouseDown:
OnMouseDown is called when the user has pressed the mouse button while over the GUIElement or Collider.
This means that if your script contains the OnMouseDown function:
private void OnMouseDown()
{
}
you can add this script to several different objects. If you click on object1, the function will be called only for object1. If you click on object2, the function will be called for object2. So you don't need to check which object is clicked.
Your script should look like this:
private void OnMouseDown()
{
// touch is a boolean, you don't need to write touch == true, it will do it automatically
// Also, instead of nesting all the code if touch == false, we just stop if it is true, so the code is easier to read
if (touch)
return;
touch = true;
Debug.Log (touch);
// transform is the transform of the object that has the script
// you don't need any check
transform.localScale += new Vector3 (3.0f, 0f, 0.1f);
}
I hope this helps
Good luck

Related

Activating animation when within radius

I am trying to create a script for my enemy turret, but it is not going well. I have a couple animations of the turret being activated and deactivated. What I need is that based on the distance from the player, it plays either animation. So once it moves inside the detection radius it plays the activation animation and once it is outside it plays the deactivation animation. Most of the other ways I try require me to create an Animation Controller, which I have little experience in using. I want a simple way to play one animation once it is inside and play a different one when it is outside. I think there was a way to store the animation clip in the script, and then play it. I have attached my current script, so you know what I mean.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyTurret : MonoBehaviour
{
public GameObject Player;
public float DistanceToPlayer;
public float DetectionRadius = 75;
// Start is called before the first frame update
void Start()
{
Player = GameObject.FindGameObjectWithTag("PlayerTank");
}
// Update is called once per frame
void Update()
{
DistanceToPlayer = Vector3.Distance(transform.position, Player.transform.position);
if (DistanceToPlayer<=DetectionRadius)
{
Debug.Log("Within Radius");
}
if (DistanceToPlayer >= DetectionRadius)
{
Debug.Log("Outside Radius");
}
}
}
Calculating and checking the distance of the player for every update() is not ideal. It will work, but it will do more work than it needs to when the player isn't even near it. Its not efficient.
What you may want to do if your player is a Rigidbody, is add a SphereCollider to the turret, set isTrigger=true, set the radius to be your detection radius, and handle the OnTriggerEnter() and OnTriggerExit() events to play or stop animations.
You can also add two public Animiation objects to the script, drag and drop your animations in the editor, then you can use animation.Play() and .Stop() etc. to control the animations.
Something similar to this. Not tested fully, but you can get the idea.
public float detectionRadius = 75;
public Animation activateAnimation;
public Animation deactivateAnimation;
void Start()
{
SphereCollider detectionSphere = gameObject.AddComponent<SphereCollider>();
detectionSphere.isTrigger = true;
detectionSphere.radius = detectionRadius;
detectionSphere.center = Vector3.zero;
}
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "PlayerTank")
{
activateAnimation.Play();
}
}
private void OnTriggerExit(Collider other)
{
if (other.gameObject.tag == "PlayerTank")
{
deactivateAnimation.Play();
}
}
Your animations must not loop otherwise you will have to add more logic to check if animation.isPlaying and do your own animation.Stop() etc.

Text is supposed to be displayed when childCount equals 0, but it doesn't

this should be a very simple answer. I'm following a Unity with C# tutorial for making a simple Space Invaders game, and at one point it is shown that when our enemyHolder object has no child objects left (when all enemies are destroyed) the attached text under the winText function should be displayed.
So we have
if (enemyHolder.childCount == 0)
{
winText.enabled = true;
}
When I run the code the text isn't displayed after the enemies are destroyed and no child object is left. It's like the code stops getting read at that point, although the character is still movable and you can generate new shots.
If I create two "Enemy" child objects and tell it to display the winText rather when the childCount reaches 1, it does work.
So why is it not working when the function calls for == 0?
EDIT: Complete class code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class EnemyController : MonoBehaviour
{
private Transform enemyHolder;
public float speed;
public GameObject shot;
public Text winText;
public float fireRate = 0.997f;
// Start is called before the first frame update
void Start()
{
winText.enabled = false;
InvokeRepeating("MoveEnemy", 0.1f, 0.3f);
enemyHolder = GetComponent<Transform>();
}
void MoveEnemy()
{
enemyHolder.position += Vector3.right * speed;
foreach (Transform enemy in enemyHolder)
{
if (enemy.position.x < -10.5 || enemy.position.x > 10.5)
{
speed = -speed;
enemyHolder.position += Vector3.down * 0.5f;
return;
}
if (enemy.position.y <= -4)
{
GameOver.isPlayerDead = true;
Time.timeScale = 0;
}
if (enemyHolder.childCount == 1)
{
CancelInvoke();
InvokeRepeating("MoveEnemy", 0.1f, 0.25f);
}
if (enemyHolder.childCount == 0)
{
winText.enabled = true;
}
}
}
}
Your code is inside the void MoveEnemy() function.
I'm assuming your script is attached to in-game enemies. Your code doesn't run because the MoveEnemy function no longer runs if there are no enemies.
So, you need to handle enemy movement and scene handling in different scripts.
The code that checks the enemy holder's number of children should be placed inside a void Update() function. This Update() function should be placed on an object that never gets deleted. Its advantage is that it runs every frame.
As a convention, devs generally use separate empty objects or even the camera to attach scripts which contain Update functions that handle the scene. Good luck!
Read more on Update

Unity 2D, Shooting with bow Instantiate problem

Hi everyone I have a little problem with my bow shooting script. I mean i want to shoot an arrow when play the one of last frames from my animation. I try to do it by setting an firePoint GameObject, put it by recording in my Animation Tab in desired frame. It's of course disabled but its enabled when animation plays and then its again disabled. So the problem is:
- When i hit button which match my Shooting input, the animation plays,
- My Instantiate appears and it produces multiple arrows,
- When its disabled it stops to produce arrows.
I want to produce only one arrow. Could anyone help?
CombatScript.cs:
/* private bool shootBow;
* public bool needReload = false;
* public float reloadTime = 1.5f;
* public float realoadCD;
*/
public void RangeAttack()
{
if (needReload == false && gameObject.GetComponent<PlayerControls>().grounded == true && Input.GetButtonDown("Ranged"))
{
animator.SetTrigger("shootBow");
attack1 = false; // Melle attacks sets to false in case of lag or smth.
attack2 = false;
attack3 = false;
needReload = true;
if (needReload == true)
{
reloadCD = reloadTime;
}
}
if (reloadCD > 0 && needReload == true)
{
reloadCD -= Time.deltaTime;
}
if (reloadCD <= 0)
{
reloadCD = 0;
needReload = false;
}
if (firePoint.gameObject.activeSelf == true)
{
Instantiate(Missile, new Vector3(firePoint.position.x + 1, firePoint.position.y), firePoint.rotation);
Debug.Log("It's a bird, a plane, no.. it's arrow.");
}
}
Arrow Controller.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ArrowController : MonoBehaviour {
public float speed;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update ()
{
GetComponent<Rigidbody2D>().velocity = new Vector2(speed, GetComponent<Rigidbody2D>().velocity.y);
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.tag == "Enemy")
{
Destroy(collision.gameObject);
}
Debug.Log("Arrow Broke");
Debug.Log(gameObject.name);
//Destroy(gameObject);
}
public void OnCollisionEnter2D(Collision2D collision)
{
}
}
Example of my situation:
Example of true/false needReload statement:
in right Inspector you have my Player informations, in left (or bottom) Inspector you have Missile (Arrow) inspector
You can use Animation Events and for example a boolean that is your firerate.
And you Code can look something like this:
float LastShoot;
float FireRate = 10;
public void OnShoot()
{
if (LastShoot <= 0)
{
//Shoot your arrow
LastShoot = FireRate;
}
}
void Update()
{
LastShoot -= 0.5f;
}
But i don't know if this is the best solution to calculate a firerate. If someone knows a better one feel free and edit my awnser :)
Okey... couple of hours later (which I wasted...). The answer was ridiculously easy. For future "problems" like that... the thing was to create in my script function called "ProduceArrow()"and (additionaly to what i did) something like Animation Event it's in Animation Tab, when you create your animation timeline you just need to call it clicking right mouse button and then choose it and pick proper function.
Some feedback - gif
The way your code works right now, Instatiate will be called every frame. So one button click, which is probably longer than one frame, will trigger multiple arrows, so you need to set a condition for when you want Instantiate to be called. You already calculate a reload time, which is exactly what you need. You just need to include it in your if-statement like follows:
if (firePoint.gameObject.activeSelf == true && !needReload)
{
Instantiate(Missile, new Vector3(firePoint.position.x + 1, firePoint.position.y), firePoint.rotation);
Debug.Log("It's a bird, a plane, no.. it's arrow.");
}

Pan Tool Algorithm being very jumpy

Looking for a bit of help to see how I can make this algorithm a little better. So I have some code for a pan tool that moves the camera based on the user dragging the mouse to move the camera around. However it is really jumpy unless it is done in really short drags. I.e. the objects in the scene appear to vibrate, which is more likely to be the camera and canvas 'vibrating' rather than the objects themselves.
The code works by toggling a dragging boolean using the SystemEvent methods OnPointerDown and OnPointerUp and assigns a MouseStart Vector3 in world coordinates and also a CameraStart Vector3.
public void OnPointerDown(PointerEventData EventData)
{
if (!dragging)
{
dragging = true;
MouseStart = new Vector3(Camera.main.ScreenToWorldPoint(Input.mousePosition).x, Camera.main.ScreenToWorldPoint(Input.mousePosition).y, 0f);
CameraStart = TheCamera.transform.position;
}
}
public void OnPointerUp(PointerEventData EventData)
{
if (dragging)
{
dragging = false;
}
}
Then in the update loop while the dragging variable is true, xChange and yChange float values are determined based on the current mouse position compared to the original mouse position, the camera position is then adjusted according to these. My thought process was that because it is relative to a fixed MouseStart (because it is only changed in the single frame where the pointer is clicked and dragging = 0) that if I were to drag and then say keep the mouse still, there would be no change in coordinates as it'd be repeatedly putting the Camera in the same position. The full code looks like this:
private bool dragging;
private string CurrentTool;
private ButtonController[] DrawingTools;
public Camera TheCamera;
public Vector3 MouseStart;
public Vector3 CameraStart;
public float sensitivity;
// Use this for initialization
void Start () {
TheCamera = FindObjectOfType<Camera>();
DrawingTools = FindObjectsOfType<ButtonController>();
}
// Update is called once per frame
void Update () {
for (int i = 0; i < DrawingTools.Length; i++)
{
if (DrawingTools[i].Pressed)
{
CurrentTool = DrawingTools[i].gameObject.name;
}
}
if (dragging && CurrentTool == "PanTool Button")
{
float xChange;
float yChange;
Vector3 MousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
if (MousePosition.x > MouseStart.x)
{
xChange = -Mathf.Abs(MousePosition.x - MouseStart.x);
}
else
{
xChange = Mathf.Abs(MousePosition.x - MouseStart.x);
}
if (MousePosition.y > MouseStart.y)
{
yChange = -Mathf.Abs(MousePosition.y - MouseStart.y);
}
else
{
yChange = Mathf.Abs(MousePosition.y - MouseStart.y);
}
TheCamera.transform.position = new Vector3(CameraStart.x + xChange*sensitivity, CameraStart.y + yChange*sensitivity, CameraStart.z);
}
}
public void OnPointerDown(PointerEventData EventData)
{
if (!dragging)
{
dragging = true;
MouseStart = new Vector3(Camera.main.ScreenToWorldPoint(Input.mousePosition).x, Camera.main.ScreenToWorldPoint(Input.mousePosition).y, 0f);
CameraStart = TheCamera.transform.position;
}
}
public void OnPointerUp(PointerEventData EventData)
{
if (dragging)
{
dragging = false;
}
}
Any help is appreciated, thanks.
EDIT: Just to clarify this is a 2d environment
This is happening because the Camera from which you are determining the world position of the mouse is being updated every frame according to the world position of the mouse, which causes a feedback loop (and therefore noise + jitter).
You can reduce noise from the feedback loop by smoothing the Camera's movement over time (effectively a low pass), or try to remove the feedback loop entirely by altering your calculations so the camera position and target position (mouse) don't rely on each other - although I'm not sure how to go about that if it's actually possible for your intent.
Check out Vector3.SmoothDamp.
Gradually changes a vector towards a desired goal over time.
The vector is smoothed by some spring-damper like function, which will
never overshoot. The most common use is for smoothing a follow camera.

Transferring a Vector3 variable from one script (WayPointPositioner) to another and change it into a transform (Walking)

I'm having a bit of trouble getting the Vector3 wayPointPosition to my other script called Walking and changing it into the Transform target. My troubles lie in the fact that I'm trying to grab this dynamic variable from WayPointPositioner (it changes depending on what object is clicked in the stage and whether the player overlaps with this waypoint) and import and use it in another script.
Below is the code I'm using.
WayPointPositioner
using UnityEngine;
using System.Collections;
public class WayPointPositioner : MonoBehaviour {
public Vector3 wayPointPosition = Vector3.zero;
private bool checkPlayerWaypointCollision;
void Start()
{
}
void OnTriggerStay2D (Collider2D other)
{
// Check if collision is occuring with player character.
if (other.gameObject.name == "Player")
{
checkPlayerWaypointCollision = true;
}
else
{
checkPlayerWaypointCollision = false;
}
}
//Check if object is clicked
void OnMouseDown ()
{
// If its the player, then return a new position for the player to move to for walking
// Else debug that its not so
if (checkPlayerWaypointCollision == false)
{
Debug.Log ("Object not colliding and retrieving position");
Debug.Log (wayPointPosition);
Debug.Log (gameObject.name);
wayPointPosition = new Vector3 (transform.position.x, transform.position.y, 10);
wayPointPosition = Camera.main.ScreenToWorldPoint(wayPointPosition);
}
else
{
Debug.Log ("Object is colliding, no movement needed");
}
}
}
Walking
using UnityEngine;
using System.Collections;
public class Walking : MonoBehaviour {
public Transform target;
public WayPointPositioner wayPointPosition;
public bool walkingAnimation = false;
private Animator anim;
void Awake ()
{
anim = GetComponent<Animator> ();
wayPointPosition = GameObject.FindGameObjectWithTag ("Waypoint").GetComponent<WayPointPositioner> ();
}
void Start ()
{
}
void Update ()
{
Debug.Log ("This is in Walking, WPP =" + wayPointPosition);
}
}
As you can see I'm trying to import the wayPointPosition from the seperate class which is attached to the gameobjects called "Waypoint" (In my current layout those are empty objects with circle colliders to check if they have been clicked). However when I run this, I am not getting my Vector, but I'm getting the name of the last waypoint in the hierarchy (I have currently 6 waypoints which can be clicked) and not a Vector.
I hope someone is able to help me / point out my mistake. I'm still learning C# so I might've made a strange / odd assumption which isn't working.
Kind regards,
Veraduxxz.
It looks like invoking GameObject.FindGameObjectWithTag("Waypoint").GetComponent<WayPointPositioner>(); retrieves a component from the game object which matches the specified tag, as well as a type argument T which derives from MonoBehavior.
Calling this should actually give you an instance of your WayPointPositioner class, which you can then pass to whichever methods you want, and interact with its Vector3 however you would like.

Categories