Edit : I need an answer for a 2D implementation, not 3D. So the 'Asked Before' question doesn't work for my project.
Edit : Using the accepted answer below, this is my working script. It's slightly off because the 0.1F part of the Vector3 is hard to get right, but it works. Time.deltaTime didn't work though.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraZoom : MonoBehaviour
{
private float zoom = 10;
Vector3 newPosition;
void Update()
{
if (Input.GetAxis("Mouse ScrollWheel") > 0 && zoom > 9)
{
zoom -= 1;
Camera.main.orthographicSize = zoom;
newPosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
transform.position = Vector3.Lerp(transform.position, newPosition, 0.1F);
}
if (Input.GetAxis("Mouse ScrollWheel") < 0 && zoom < 101)
{
zoom += 1;
Camera.main.orthographicSize = zoom;
}
}
}
Original Question : I have a basic script for zooming in and out, but when I zoom back in with the mouse-wheel, I want to zoom in to the point of the mouse cursor. I think you can see something similar in the Paradox Games (EU4 etc), where you zoom out, hold your mouse over a country off to the left of the screen, then zoom in and the country zooms in and becomes centered.
Here's my basic script so far, attached to my camera, which just zooms in and out in a straight line.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraZoom : MonoBehaviour {
public float zoom = 10F;
void Update () {
if (Input.GetAxis("Mouse ScrollWheel") > 0 && zoom > 9)
{
zoom -= 1;
}
if (Input.GetAxis("Mouse ScrollWheel") < 0 && zoom < 101)
{
zoom += 1;
}
GetComponent<Camera>().orthographicSize = zoom;
}
}
I haven't tried this myself but I hope this will work.
Vector3 newPosition;
bool canZoom;
bool isMoving;
void Update ()
{
if (Input.GetAxis("Mouse ScrollWheel") > 0 && zoom > 9)
{
zoom -= 1;
canZoom = true;
}
if (Input.GetAxis("Mouse ScrollWheel") < 0 && zoom < 101)
{
zoom += 1;
canZoom = true;
}
if (canZoom)
{
isMoving = true;
Camera.main.orthographicSize = zoom;
newPosition = Camera.main.ScreenToWorldPoint (Input.mousePosition);
}
if (isMoving)
{
transform.position = Vector3.Lerp (transform.position, newPosition, Time.deltaTime);
}
if(transform.position == newPosition)
{
isMoving = false;
}
}
Note: If you want the camera to maintain certain camera height, adjust newPosision's y value before moving.
Hope this helps.
Calculate the difference in world space and move towards it while zooming.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraZoom : MonoBehaviour {
public float zoom = 10F;
void Update () {
if (Input.GetAxis("Mouse ScrollWheel") > 0 && zoom > 9)
{
zoom -= 1;
}
if (Input.GetAxis("Mouse ScrollWheel") < 0 && zoom < 101)
{
zoom += 1;
}
Camera cam = GetComponent<Camera>();
RaycastHit hit;
Ray ray = cam.ScreenPointToRay(Input.mousePosition);
Vector3 targetPos;
if (Physics.Raycast(ray , out hit))
{
targetPos = hit.point;
}
Camera cam = GetComponent();
cam.orthographicSize = zoom;
cam.transform.position += (targetPos - transform.position) / 5f;
}
}
Related
I have a script for my camera controls and the panning seems to be difficult to get working, ScreenToWorldPoint() is returning weird values and while the mouse is not even being moved.
The panning should have the world stick the cursor (Kind of like the unity editor it's self)
Create a CS file called "CameraControls" and paste the code below, asign it to a camera, and try to pan using middle click, It will move the camera crazily.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraControls : MonoBehaviour {
public float ZoomSpeed = 0.5f;
public float MinZoom = 0.5f;
public float MaxZoom = 20f;
// Update is called once per frame
void Update() {
// Zoom out
if (Input.GetAxis("Mouse ScrollWheel") < 0) {
if (Camera.main.orthographicSize < MaxZoom) {
Camera.main.orthographicSize += ZoomSpeed;
}
}
// Zoom in
if (Input.GetAxis("Mouse ScrollWheel") > 0) {
if (Camera.main.orthographicSize > MinZoom) {
Camera.main.orthographicSize -= ZoomSpeed;
}
}
// Pan
if (Input.GetMouseButton(2)) {
Vector3 mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition); //<-- Problem
transform.position = new Vector3(mousePos.x, mousePos.y, transform.position.z);
}
}
}
I've been working on a 2D top-down space shooter game in Unity. I am currently coding the enemy AI, which will be able to follow the player, while also keeping its distance. I am running into a problem where the AI always teleports back to 0 on the Z-axis, which makes the Enemy invisible. Here is my whole enemy script so far.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Enemy : MonoBehaviour
{
float speed = 5;
float stoppingDistance = 4;
float retreatDistance = 2;
Transform player;
void Start()
{
player = GameObject.FindGameObjectWithTag("Player").transform;
}
void Update()
{
if(Vector2.Distance(transform.position, player.position) > stoppingDistance)
{
transform.position = Vector2.MoveTowards(transform.position, player.position, speed * Time.deltaTime);
} else if(Vector2.Distance(transform.position, player.position) < stoppingDistance && Vector2.Distance(transform.position, player.position) > retreatDistance) {
transform.position = this.transform.position;
} else if(Vector2.Distance(transform.position, player.position) < retreatDistance) {
transform.position = Vector2.MoveTowards(transform.position, player.position, -speed * Time.deltaTime);
}
}
}
Thank you!
it's been a while since I done unity and its mega late where I'am. But hopefully this helps you get on track.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Enemy : MonoBehaviour
{
public Transform Player;
float MoveSpeed = 4;
float MoveSpeed2 = MoveSpeed + 2;
float MaxDist = 4;
float MinDist = 2;
void Start()
{
}
void Update()
{
transform.LookAt(Player);
if (Vector2.Distance(transform.position, Player.position) > MinDist && Vector2.Distance(transform.position, Player.position) < MaxDist)
{
//continue aproach
transform.position += transform.forward * MoveSpeed * Time.deltaTime;
} else if ( Vector2.Distance(transform.position, Player.position) >= MaxDist && Vector2.Distance(transform.position, Player.position) > MinDist )
{
// get closer
transform.position += transform.forward * MoveSpeed2 * Time.deltaTime;
}
else if(Vector2.Distance(transform.position, player.position) <= MinDist && Vector2.Distance(transform.position, Player.position) < MaxDist) {
// Retreat
transform.position += transform.backward * MoveSpeed2 * Time.deltaTime;
}
}
}
So I've been following a tutorial on making a 2D game in Unity (I'm a complete newbie, this is my first contact with programming), and I wanted to add a feature to the game(bold).
The "heart system" that I added works correctly (the number of empty hearts is equal to damage taken by player), but it caused my player to transform his position in a wierd way. You can see, that there are boundries set (maxHeight =3,2, minHeight=-3,2), and the value of his movement as well (Yincrement = 3.2) and yet, after pressing up or down arrowkeys he seems to change Y position by around 4.67.
Here's the player script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class Player : MonoBehaviour
{
private Vector2 targetPos;
public float Yincrement;
public float speed;
public float maxHeight;
public float minHeigth;
public int health = 3;
public int numOfHearts;
public Image[] hearts;
public Sprite heartFull;
public Sprite heartEmpty;
public GameObject effect;
public Image healthDisplay;
private void Update()
{
for (int i = 0; i < hearts.Length; i++)
{
if (i < health)
{
hearts[i].sprite = heartFull;
}
else
{
hearts[i].sprite = heartEmpty;
if (i < numOfHearts)
{
hearts[i].enabled = true;
}
else
{
hearts[i].enabled = false;
}
}
if (health <= 0)
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
}
transform.position = Vector2.MoveTowards(transform.position, targetPos, speed * Time.deltaTime);
if (Input.GetKeyDown(KeyCode.UpArrow) && transform.position.y < maxHeight)
{
Instantiate(effect, transform.position, Quaternion.identity);
targetPos = new Vector2(transform.position.x, transform.position.y + Yincrement);
}
else if (Input.GetKeyDown(KeyCode.DownArrow) && transform.position.y > minHeigth)
{
Instantiate(effect, transform.position, Quaternion.identity);
targetPos = new Vector2(transform.position.x, transform.position.y - Yincrement);
}
}
}
}
Main Issue
The main problem is your for loop. It should only be used for updating the UI - not for multiple times calling your movement and Scene reload! You should close your for loop earlier.
for (int i = 0; i < hearts.Length; i++)
{
// you can also reduce these to single lines
hearts[i].enabled = i < numOfHearts;
if(i < numOfHearts) hearts[i].sprite = i < health ? heartFull : heartEmpty;
} // <-- ALREADY CLOSE IT HERE
if(health <= 0) ...
...
Position clamping
Your clamping of the position is extremely insecure! Imagine the current position being 3.1 which is still < maxHeight so you add the Yincrement once and it results in a maximum possible height of 6.3! That's not what you wanted.
You should clamp directly on the targetPosition not on the current transform.position. You could for example use Mathf.Clamp for making sure the targetPos.y always stays within the given range.
Also since both cases do something very similar I would reduce this to only one movement using a simply int variable for setting the direction:
...
transform.position = Vector2.MoveTowards(transform.position, targetPos, speed * Time.deltaTime);
var move = 0;
var targetPosY = targePos.y;
if (Input.GetKeyDown(KeyCode.UpArrow) && targetPosY < maxHeight)
{
move = 1;
}
else if (Input.GetKeyDown(KeyCode.DownArrow) && targetPosY > minHeigth)
{
move = -1;
}
// Handle the movement according to the direction
// in one single code block for both directions
if(move != 0)
{
Instantiate(effect, transform.position, Quaternion.identity);
// increase/decrease the targetPosY according to the move direction
targetPosY += move * Yincrement;
// Now make sure it is within the range
targetPosY = Mathf.Clamp(targetPosY, minHeight, maxHeight);
// finally assign the new target position
targetPos = new Vector2(transform.position.x, targetPosY);
}
Could you please specify in detail what your game is about and what you are trying to do in each part of the script. I might be able to help you then. Also, if this is your first contact with programming, this is way to advanced. Start with something simpler and first understand the basic concepts of programming before moving on. Here is a good tutorial series to learn c# programming for absolute beginners.
https://www.youtube.com/watch?v=pSiIHe2uZ2w
I am not sure what you are trying to do but why is your movement control within your for loop. That might be why your are messing up. Try removing all of this code out of the for loop.
transform.position = Vector2.MoveTowards(transform.position, targetPos, speed * Time.deltaTime);
if (Input.GetKeyDown(KeyCode.UpArrow) && transform.position.y < maxHeight)
{
Instantiate(effect, transform.position, Quaternion.identity);
targetPos = new Vector2(transform.position.x, transform.position.y + Yincrement);
}
else if (Input.GetKeyDown(KeyCode.DownArrow) && transform.position.y > minHeigth)
{
Instantiate(effect, transform.position, Quaternion.identity);
targetPos = new Vector2(transform.position.x, transform.position.y - Yincrement);
}
Also this code should be after you check if the player has moved.
transform.position = Vector2.MoveTowards(transform.position, targetPos, speed * Time.deltaTime);
Here is a tip to make movement a little easier. Use transform.translate instead of Vector2.MoveTowards. Transform.translate takes in 3 floats and changes you player's position by the three floats represented as a vector.
Here is an example
transform.translate(0,2,0);
This will change the players y position by 2. I thing your code should look like this.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class Player : MonoBehaviour
{
private Vector2 targetPos;
public float Yincrement;
public float speed;
public float maxHeight;
public float minHeigth;
public int health = 3;
public int numOfHearts;
public Image[] hearts;
public Sprite heartFull;
public Sprite heartEmpty;
public GameObject effect;
public Image healthDisplay;
private void Update()
{
for (int i = 0; i < hearts.Length; i++)
{
if (i < health)
{
hearts[i].sprite = heartFull;
}
else
{
hearts[i].sprite = heartEmpty;
if (i < numOfHearts)
{
hearts[i].enabled = true;
}
else
{
hearts[i].enabled = false;
}
}
}
if (health <= 0)
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
}
if (Input.GetKeyDown(KeyCode.UpArrow) && transform.position.y < maxHeight)
{
transform.Translate(0,Yincrement,0)
}
else if (Input.GetKeyDown(KeyCode.DownArrow) && transform.position.y > minHeigth)
{
Instantiate(effect, transform.position, Quaternion.identity);
transform.Translate(0,Yincrement,0);
}
}
}
After I receive your reply, I possibly could help but there are no guarantees because I only have about a years worth of experience too.
I have a graph in unity, I want to drag it just up-down and left-right to see upcoming coordinates.
I wrote this code to drag the graph object but it drags it all around without a limit. I just want it to go just up-down and left-right.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DragGraph : MonoBehaviour {
float OffsetX;
float OffsetY;
public void BeginDrag(){
OffsetX = transform.position.x - Input.mousePosition.x;
OffsetY = transform.position.y - Input.mousePosition.y;
}
public void OnDrag(){
transform.position = new Vector3 (OffsetX + Input.mousePosition.x, OffsetY + Input.mousePosition.y);
}
}
if OffsetX > OffsetY, don't use Input.mousePosition.y
And vice versa.
Figuring out what to do when OffsetX == OffsetY is left as an exercise to the reader.
If you want it to go up and down then why are you using the x value? You could just leave it 0 it and only the y value will change and make it go up and down.
Edit
Not sure if this is ideal but its similar to a swipe up, down, left and right.I tried it and it goes in one direction at a time.
Vector2 offset;
Vector2 startPos = Vector2.zero;
public void OnBeginDrag(PointerEventData eventData)
{
startPos = eventData.position;
offset.x = transform.position.x - Input.mousePosition.x;
offset.y = transform.position.y - Input.mousePosition.y;
}
public void OnDrag(PointerEventData eventData)
{
Vector2 direction = eventData.position - startPos;
direction.Normalize();
if ((direction.x>0 || direction.x<0) && direction.y>-0.5f && direction.y < 0.5f)
{
transform.position = new Vector3(offset.x+ Input.mousePosition.x, transform.position.y);
}
if ((direction.y > 0 || direction.y<0) && direction.x > -0.5f && direction.x < 0.5f)
{
transform.position = new Vector3( transform.position.x, offset.y + Input.mousePosition.y);
}
}
I'm trying to determine between tapping the screen and swiping the screen.
I want to determine swipes from left, right, up and down.
Right now when I swipe left or right, it also turns my player.
Which isn't suppose to happen. It should be one or the other, either turn or move left or right. My question is how can I determine all five things?
Swipe left, right, up, and down as-well as just tapping the screen.
Here is my code
using UnityEngine;
using System.Collections;
public class PlayerMotor : MonoBehaviour {
private CharacterController controller;
private Vector3 moveVector;
private float speed = 2.0f;
private float verticalVelocity = 0.0f;
private float gravity = 12.0f;
public Touch touch;
private void Start() {
controller = GetComponent<CharacterController> ();
}
private void Update()
{
moveVector = Vector3.zero;
if (controller.isGrounded)
{
verticalVelocity = -0.5f;
}
else
{
verticalVelocity -= gravity * Time.deltaTime;
}
if (Input.GetMouseButton (0))
{
if (touch.position.x == touch.deltaPosition.x && touch.position.x == touch.deltaPosition.x)
{ //3px accuracy, stationery :P
moveVector.x = transform.forward.x * speed;
transform.Rotate (new Vector3 (0, -90, 0));
}
else if (touch.position.x != touch.deltaPosition.x && touch.position.x != touch.deltaPosition.x)
{
if (Input.mousePosition.x > Screen.width / 2)
moveVector.x = speed;
else
moveVector.x = -speed;
}
}
moveVector.y = verticalVelocity;
moveVector.z = transform.forward.z * speed;
controller.Move (moveVector * Time.deltaTime);
}
}
I found this code with a little bit of searching. (Here)
Adding an else statement after all the if (currentSwipe...) statements should work to detect the click
//inside class
Vector2 firstPressPos;
Vector2 secondPressPos;
Vector2 currentSwipe;
public void Swipe()
{
if(Input.touches.Length > 0)
{
Touch t = Input.GetTouch(0);
if(t.phase == TouchPhase.Began)
{
//save began touch 2d point
firstPressPos = new Vector2(t.position.x,t.position.y);
}
if(t.phase == TouchPhase.Ended)
{
//save ended touch 2d point
secondPressPos = new Vector2(t.position.x,t.position.y);
//create vector from the two points
currentSwipe = new Vector3(secondPressPos.x - firstPressPos.x, secondPressPos.y - firstPressPos.y);
//normalize the 2d vector
currentSwipe.Normalize();
//swipe upwards
if(currentSwipe.y > 0 currentSwipe.x > -0.5f currentSwipe.x < 0.5f)
{
Debug.Log("up swipe");
}
//swipe down
if(currentSwipe.y < 0 currentSwipe.x > -0.5f currentSwipe.x < 0.5f)
{
Debug.Log("down swipe");
}
//swipe left
if(currentSwipe.x < 0 currentSwipe.y > -0.5f currentSwipe.y < 0.5f)
{
Debug.Log("left swipe");
}
//swipe right
if(currentSwipe.x > 0 currentSwipe.y > -0.5f currentSwipe.y < 0.5f)
{
Debug.Log("right swipe");
}
}
}
}