I am working on a Pong-Clone for Android right now and have watched a tutorial about it. It works perfectly with the keyboard, but how do I get a similar control on the mobile phone. My current code looks like this:
private void Start()
{
ball = GameObject.FindGameObjectWithTag("Ball").GetComponent<Ball>();
col = GetComponent<BoxCollider2D>();
if (side == Side.Left)
forwardDirection = Vector2.right;
else if (side == Side.Right)
forwardDirection = Vector2.left;
}
private void Update()
{
if (!overridePosition)
MovePaddle();
}
private void MovePaddle()
{
float targetYPosition = GetNewYPosition();
ClampPosition(ref targetYPosition);
transform.position = new Vector3(transform.position.x, targetYPosition, transform.position.z);
}
private void ClampPosition(ref float yPosition)
{
float minY = Camera.main.ScreenToWorldPoint(new Vector3(0, 0)).y;
float maxY = Camera.main.ScreenToWorldPoint(new Vector3(0, Screen.height)).y;
yPosition = Mathf.Clamp(yPosition, minY, maxY);
}
private float GetNewYPosition()
{
float result = transform.position.y;
if (isAI)
{
if (BallIncoming())
{
if (firstIncoming)
{
firstIncoming = false;
randomYOffset = GetRandomOffset();
}
result = Mathf.MoveTowards(transform.position.y, ball.transform.position.y + randomYOffset, moveSpeed * Time.deltaTime);
}
else
{
firstIncoming = true;
}
}
else
{
float movement = Input.GetAxisRaw("Vertical " + side.ToString()) * moveSpeed * Time.deltaTime;
result = transform.position.y + movement;
}
return result;
}
I want the player to move on the y Axis, but I have no idea how I can do it with touch controls (I am new to games). I would really appreciate any help, so I can continue programming :)
I'm going to assume what you want isove the padel by touch and drag up or down.
I would keep your code for debugging and add a switch to only use touch if touch is supported and your code otherwise.
// Adjust via the Inspector how much the user has to drag in order to move the padel
[SerializeField] private float touchDragSensitivity = 1;
private float lastTouchPosY;
// Make sure to store the camera
// Using Camera.main is quite expensive!
[SerializeField] private Camera _mainCamera;
private void Awake()
{
if(!_mainCamera) _mainCamera = GetComponent<Camera>();
...
}
private float GetNewPosition()
{
...
else
{
var movement = 0f;
if(!Input.touchSupported)
{
// For non-touch device keep your current code
movement = Input.GetAxisRaw("Vertical " + side.ToString()) * moveSpeed * Time.deltaTime;
}
else
{
if(Input.touchCount > 0)
{
var touch = Input.GetTouch(0);
var touchPositionY = _mainCamera.ScreenToWorldPoint(touch.position).y;
if(touch.phase = TouchPhase.Moved)
{
var delta = touchPositionY - lastTouchPosY;
movement = delta * touchDragSensitivity;
}
lastTouchPosY = touchPositionY;
}
}
result = transform.position.y + movement;
}
}
Or alternatively if you rather wanted a similar experience as for the button input you could also only check whether the touch is on the bottom or top half of the screen and move the padel constantly like with your original code before:
private void Update()
{
...
else
{
var movement = 0f;
if(!Input.touchSupported)
{
// For non-touch device keep your current code
movement = Input.GetAxisRaw("Vertical " + side.ToString()) * moveSpeed * Time.deltaTime;
}
else
{
if(Input.touchCount > 0)
{
var touch = Input.GetTouch(0);
var touchPositionY = touch.position.y;
movement = (touchPositionY >= Screen.height / 2f ? 1 : -1) * moveSpeed * Time.deltaTime;
}
}
result = transform.position.y + movement;
}
}
Update
As to your question
is there some way to split the screen so two players can play on the same screen
Yes: You can add a check on which side a touch is using touch.position.x >= screen.width / 2f => right side.
You could e.g. filter for valid touches like
if(Input.touchCount > 0)
{
if(side == Side.Right)
{
var validTouches = Input.touches.Where(t => t.position >= Screen.width / 2f).ToArray();
if(validTouches.Length > 0)
{
var touchPositionY = validTouches[0].position.y;
movement = (touchPositionY >= Screen.height / 2f ? 1 : -1) * moveSpeed * Time.deltaTime;
}
}
else
{
var validTouches = Input.touches.Where(t => t.position < Screen.width / 2f).ToArray();
if(validTouches.Length > 0)
{
var touchPositionY = validTouches[0].position.y;
movement = (touchPositionY >= Screen.height / 2f ? 1 : -1) * moveSpeed * Time.deltaTime;
}
}
}
If you want to press and hold make the object move you can make two buttons one for +y and the other for -y. then use Input.GetTouch to register the players input.
Related
hi there thanks for taking the time to look at my question
im currenty making a 2D side scroller/platformer and im currently facing a issue where my enemies are piling up on top of each other. through googling i see alot of Astar pathfinding solving this however i hesitant to jump into ai right now as i glanced over its internal scripts and they are outside my current knowledge and im still attempting to get a understanding of just using scripting to achieve what i want.
i currently have my enemies correctly chasing my player with a vector2.movetowards however eventually they all pile on each other.
through google i have found this is a typical outcome of movetowards and i found this solution
https://answers.unity.com/questions/1608266/stop-enemies-from-grouping-up.html
and i implemented its code
void avoidOthers()
{
var hits = Physics2D.OverlapCircleAll(enemy.transform.position, avoidenceRadius);//create a circle around each enemy and check to see if they are not colliding
foreach (var hit in hits)
{
if(hit.GetComponent<DartFish>() != null && hit.transform != transform)//check to see if it is a enemy
{
Debug.Log("enemy transform" + enemy.transform.position);
Debug.Log("hit transform" + hit.transform.position);
Vector2 difference = enemy.transform.position - hit.transform.position;//check how close we are to another enemy
difference = difference.normalized / Mathf.Abs(difference.magnitude);//calculate how long the vector is
sum += difference;//add together differences so we can calculate a average of the group
count++;
}
}
if(count > 0)// if we found enemys interfearing with each other
{
sum /= count;//average of the movement
sum = sum.normalized * avoidenceSpeed;//calculate movement speed
Debug.Log("sum = " + sum);
enemy.transform.position = Vector2.MoveTowards(enemy.transform.position, enemy.transform.position + (Vector3)sum, avoidenceSpeed * Time.deltaTime);
}
}
however i needed to add "enemy" to each transform.position or they would instantly snap from my spawn location to the player.
after adding enemy they stopped snapping over a large distance except now they started piling again
all of my debug logs look like they are printing out the correct values
i was also reading through transform.translate as looking at my "sum" values this should be how much they are moving away from each other
so i changed the line
enemy.transform.position = Vector2.MoveTowards(enemy.transform.position, enemy.transform.position + (Vector3)sum, avoidenceSpeed * Time.deltaTime);
to
transform.Translate((Vector3)sum * avoidenceSpeed * Time.deltaTime, Space.World);
now it seems to only effect one fish and the others clump together still
from my understanding physics.overlapcircle all should be functioning as a list?
heres my entire code albeit long(still relatively new to coding)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DartFish : MonoBehaviour
{
public Transform target;
public float movementSpeed;
private float initialMovementSpeed;
public float dashSpeed;
private float spawnSpeed = 1.5f;
public float chaseRadius;
private float initialChaseRadius;
public float dashRadius;
private Vector2 targetDirection;
private float directionX;
private float previousDirectionX;
private float directionY;
private float spriteAngle;
private float previousAngle;
private float currentSpriteAngle;
private float difference;
private SpriteRenderer mySpriteRenderer;
private Rigidbody2D myRigidbody;
private bool directionIsAboutToChange;
private bool directionHasChanged;
private Vector3 angleCompensation;
// avoid others variables
public float avoidenceSpeed;
public float avoidenceRadius;
public Transform enemy;
private Vector2 sum = Vector2.zero;
private float count;
// Start is called before the first frame update
void Start()
{
enemy = GameObject.FindWithTag("dartfish").transform;
target = GameObject.FindWithTag("Player").transform;
mySpriteRenderer = GetComponent<SpriteRenderer>();
myRigidbody = GetComponent<Rigidbody2D>();
initialMovementSpeed = movementSpeed;
initialChaseRadius = chaseRadius;
}
// Update is called once per frame
void Update()
{
targetDirection = target.position - transform.position;// where is the player?
directionX = targetDirection.x;//extract only x for "check direction"
directionY = targetDirection.y;
if(directionX < -initialChaseRadius + .5f)// is the player further then the chase radius?
{
myRigidbody.velocity = new Vector2(-1.5f, 0);
}
else
{
myRigidbody.velocity = Vector2.zero;
}
if(directionX > 0)//calculate tangent angle to the player in radians and then convert it to degrees
{
spriteAngle = Mathf.Atan2(targetDirection.y, targetDirection.x) * Mathf.Rad2Deg;
}
else if(directionX < 0)//if player is to the left flip opp/adj and add 100° to zero angle
{
spriteAngle = Mathf.Atan2(targetDirection.x, targetDirection.y) * Mathf.Rad2Deg +100f;
}
currentSpriteAngle = spriteAngle;
difference = spriteAngle - previousAngle;
difference = Mathf.Abs(difference);
if(directionX < 1 && directionX > -1)//are we about to change directions?
{
//Debug.Log("direction is about to change");
directionIsAboutToChange = true;
}
if(directionIsAboutToChange == true)//did we pass through trigger zone?
{
if(directionX > 1 || directionX < -1)
//Debug.Log("direction has changed");
directionHasChanged = true;
}
lookAtMe();
chaseMe();
avoidOthers();
/*
//debug logs with spam control
if(previousAngle != spriteAngle)
{
Debug.Log("angle = " + spriteAngle);
}
if(difference != 0)
{
Debug.Log("difference = " + difference);
}
if(previousDirectionX != directionX)
{
Debug.Log("directionX = " + directionX);
}
if(previousAngle != currentSpriteAngle)
{
Debug.Log("previous angle = " + previousAngle);
}
*/
previousAngle = currentSpriteAngle;
previousDirectionX = directionX;
}
void lookAtMe()
{
if(directionHasChanged == true)//angle compensation for sprite flip
{
if(directionY > 0)//is the player above or below us
{
angleCompensation = transform.localEulerAngles;//get current rotation
//flip it
if(directionX > 0)
{
transform.localEulerAngles = -angleCompensation;
}
if(directionX < 0)
{
transform.localEulerAngles = angleCompensation;
}
}
if(directionY < 0)
{
if(directionX > 0)
{
transform.localEulerAngles = angleCompensation;
}
if(directionX < 0)
{
transform.localEulerAngles = -angleCompensation;
}
}
directionHasChanged = false;
directionIsAboutToChange = false;
}
if(directionX < -1)
{
mySpriteRenderer.flipX = true;
//Debug.Log("entered void lookAtMe.directionX <");
if(spriteAngle > previousAngle && spriteAngle < 50f )
{
transform.Rotate(0f, 0f, -difference);
}
else if(spriteAngle < previousAngle && spriteAngle > -50f)
{
transform.Rotate(0f, 0f, difference);
}
}
else if(directionX > 1)
{
mySpriteRenderer.flipX = false;
//Debug.Log("entered void lookAtMe.directionX >");
if(spriteAngle > previousAngle && spriteAngle < 50f)
{
transform.Rotate(0f, 0f, difference);
}
else if(spriteAngle < previousAngle && spriteAngle > -50f)
{
transform.Rotate(0f, 0f, -difference);
}
}
}
void avoidOthers()
{
var hits = Physics2D.OverlapCircleAll(enemy.transform.position, avoidenceRadius);//create a circle around each enemy and check to see if they are not colliding
foreach (var hit in hits)
{
if(hit.GetComponent<DartFish>() != null && hit.transform != transform)//check to see if it is a enemy
{
Debug.Log("enemy transform" + enemy.transform.position);
Debug.Log("hit transform" + hit.transform.position);
Vector2 difference = enemy.transform.position - hit.transform.position;//check how close we are to another enemy
difference = difference.normalized / Mathf.Abs(difference.magnitude);//calculate how long the vector is
sum += difference;//add together differences so we can calculate a average of the group
count++;
}
}
if(count > 0)// if we found enemys interfearing with each other
{
sum /= count;//average of the movement
sum = sum.normalized * avoidenceSpeed;//calculate movement speed
Debug.Log("sum = " + sum);
transform.Translate((Vector3)sum * avoidenceSpeed * Time.deltaTime, Space.World);
}
}
void chaseMe()
{
if(Vector3.Distance(target.position, transform.position) <= chaseRadius && Vector3.Distance(target.position, transform.position) > dashRadius)
{
transform.position = Vector3.MoveTowards(transform.position, target.position, movementSpeed * Time.deltaTime);
}
else if(Vector3.Distance(target.position, transform.position) <= dashRadius)
{
transform.position = Vector3.MoveTowards(transform.position, target.position, dashSpeed * Time.deltaTime);
}
}
}
please help me stop my enemys from clumping up without the use of AI
thanks again for any assistance and your time its much appreciated
Here is my code, if it helps anyone...
I don't really know what's wrong with it but when I searched online for an answer nothing helpful came up. It was all player controlled scripts and not anything I could use. Any help would be greatly appreciated, Thanks.
'''
[HideInInspector] public bool moving;
[HideInInspector] public int direction;
public float restTime;
public float speed = 0.1f;
private float maxX = 2f;
private float minX = -2f;
private float currentX;
void Start()
{
SpriteAnimator animator = gameObject.GetComponent<SpriteAnimator>();
currentX = gameObject.transform.position.x;
string message = "Moving: " + moving + ", Dirrection: " + direction + ", Current X: " + currentX;
Debug.Log(message);
}
void Update()
{
if (restTime > 0f)
{
restTime -= Time.deltaTime;
}
else
{
if (direction == 0 && currentX < minX)
{
moving = true;
while (currentX >= minX)
{
currentX = gameObject.transform.position.x;
gameObject.transform.position -= new Vector3(speed * Time.deltaTime, 0f, 0f);
}
moving = false;
restTime = 5f;
}
else
{
moving = true;
while (currentX <= maxX)
{
currentX = gameObject.transform.position.x;
gameObject.transform.position += new Vector3(speed * Time.deltaTime, 0f, 0f);
}
moving = false;
restTime = 5f;
}
}
}
'''
At first glance I see that if (direction == 0 && currentX < minX) should instead be if (direction == 0 && currentX > minX). Another problem that I see is that each frame would see the object be on one side or the other with no frames at positions between the two locations. If this is intended to always run with the object nearly constantly moving back and forth across the screen I would suggest trying this.
IEnumerator MoveObject() {
while(true) {
if(direction == 0) {
while(currentX >= minX) {
gameObject.transform.position -= new Vector3(speed * Time.deltaTime, 0f, 0f);
currentX = gameObject.transform.position.x;
yield return null; // waits for the next frame before continuing
}
direction == 1;
} else {
while(currentX <= maxX) {
gameObject.transform.position += new Vector3(speed * Time.deltaTime, 0f, 0f);
currentX = gameObject.transform.position.x;
yield return null; // waits for the next frame before continuing
}
direction == 0;
}
yield return new WaitForSecondsRealtime(5f); // how long to wait before continuing
// If you want to allow for the "pausing" of the coroutine by modifying the passage of in-game time, you would call
// yield return new WaitForSeconds(5f);
}
}
And you can call it from the Start Method as follows
private void Start() {
// Other Code Here //
_= StartCoroutine(MoveObject());
}
i want a karlson-esque movement for my game but i cant seem to nail it correctly, i basically want to add a force to the players base velocity just bieng tossed around in the game, like a velocity offset if you will,
for example if the player gets hit by a high velocity cube and tossed back naturally, they should struggle to regain thier stability and kind of come to a gradual halt assuming they are moving in the opposite direction than where they were being tossed, but my method (setting the players velocity) doesnt really work that well for what im trying to achieve, it simply comes to a semi-immediate halt, ive tried interpolation to make a gradual speed increase but input.getaxis and velocity both already somewhat cover that, anything more and the "coming to a halt" part works but movement isnt as snappy as i wish it was, here is my full code (note that i am somewhat of a beginner, ive done coding elsewhere but i just started unity a few weeks ago)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class charactercontroller : MonoBehaviour {
//variables and functions
public float moveSpeed = 7f;
public bool tweening = false;
public bool jumping = false;
public bool reteleporing = false;
public Vector3 smoothedvel;
public Vector3 smoothedvectorweapon;
public float smoothedweaponspeed;
public Vector3 smoothedrotationalvector;
public Quaternion smoothedrotateweapon;
public Vector3 cameraoffset;
public float[] tweeninformation = {3f,5f,6f,7f,8f,9f,10f};
public float[] tweeninformationreverse = {10f,9f,8f,7f,6f,5f,3f};
void Start () {
cameraoffset = new Vector3(0,0,0);
smoothedvel = new Vector3(0,0,0);
smoothedrotateweapon = Quaternion.Euler(0,0,0);
smoothedweaponspeed = 0.1f;
smoothedvectorweapon = new Vector3(0.559f,0,-0.523f);
StartCoroutine(waiter());
}
IEnumerator waiter()
{
while(true)
{
if (new Vector3(Input.GetAxis("Vertical"),0,Input.GetAxis("Horizontal")).magnitude != 0 && Physics.Raycast(transform.position, -Vector3.up, gameObject.GetComponent<Collider>().bounds.extents.y + 0.3f) == true)
{
gameObject.GetComponent<ParticleSystem>().Emit(new ParticleSystem.EmitParams(), 3);
transform.GetChild(0).GetComponent<AudioSource>().pitch = Random.Range(1.0f,1.5f);
transform.GetChild(0).GetComponent<AudioSource>().Play();
}
yield return new WaitForSeconds(0.25f);
}
}
void Update (){
//movement
smoothedrotationalvector = Vector3.Lerp(smoothedrotationalvector, new Vector3(-Input.GetAxis("Vertical"),0,Input.GetAxis("Horizontal")), smoothedweaponspeed);
transform.GetChild(2).GetChild(0).transform.localPosition = Vector3.Lerp(transform.GetChild(2).GetChild(0).transform.localPosition, smoothedvectorweapon, smoothedweaponspeed);
transform.GetChild(2).GetChild(0).transform.localRotation = Quaternion.Lerp(transform.GetChild(2).GetChild(0).transform.localRotation, smoothedrotateweapon, smoothedweaponspeed);
if (new Vector3(Input.GetAxis("Vertical"),0,Input.GetAxis("Horizontal")).magnitude != 0)
{
transform.GetChild(2).transform.LookAt(transform.position + smoothedrotationalvector);
}
if (transform.position.y < -100)
{
reteleporing = true;
}
if (reteleporing == true)
{
transform.position = GameObject.Find("spawnpos").transform.position;
}
if (transform.position.y > 2.4)
{
reteleporing = false;
}
///if (Physics.Raycast(transform.position,transform.forward * Time.deltaTime * Input.GetAxis("Vertical"), gameObject.GetComponent<Collider>().bounds.extents.x + 0.3f) == false && reteleporing == false)
///{
/// transform.Translate(transform.forward * Time.deltaTime * Input.GetAxis("Vertical")* moveSpeed);
///}
///else
///{
// transform.Translate(transform.forward * Time.deltaTime * Input.GetAxis("Vertical")* 1);
//}
//if (Physics.Raycast(transform.position,transform.right * Time.deltaTime * Input.GetAxis("Horizontal"),gameObject.GetComponent<Collider>().bounds.extents.z + 0.3f) == false && reteleporing == false)
//{
// transform.Translate(transform.right * Time.deltaTime * Input.GetAxis("Horizontal")* moveSpeed);
//}
//else
//{
// transform.Translate(transform.right * Time.deltaTime * Input.GetAxis("Horizontal")* 1);
//}
if (reteleporing == false)
{
gameObject.GetComponent<Rigidbody>().velocity = Vector3.Lerp(gameObject.GetComponent<Rigidbody>().velocity,new Vector3(Input.GetAxis("Horizontal") * moveSpeed,gameObject.GetComponent<Rigidbody>().velocity.y,Input.GetAxis("Vertical") * moveSpeed), 0.1f);
}
//if (Physics.Raycast(transform.position, -Vector3.up, gameObject.GetComponent<Collider>().bounds.extents.y + 0.3f) == true)
//{
// gameObject.GetComponent<Rigidbody>().velocity = Vector3.Lerp(gameObject.GetComponent<Rigidbody>().velocity, new Vector3(0,0,0), 0.01f);
//}
//camera controlling
smoothedvel = Vector3.Lerp(smoothedvel, gameObject.GetComponent<Rigidbody>().velocity * 0.1f, 0.1f);
Camera maincam = GameObject. Find("Main Camera"). GetComponent<Camera>();
Vector3 camend = transform.position + new Vector3(Input.GetAxis("Horizontal") * 0.5f,0,0);
maincam.transform.position = transform.position + new Vector3(0, 4, -7f) + cameraoffset;
maincam.transform.LookAt(transform.position + smoothedvel);
//jumping
if (Input.GetKeyDown(KeyCode.Space))
{
if (Physics.Raycast(transform.position, -Vector3.up, gameObject.GetComponent<Collider>().bounds.extents.y + 0.3f) == true)
{
gameObject.GetComponent<Rigidbody>().AddForce(transform.up * 250);
moveSpeed = 9f;
}
}
if (Physics.Raycast(transform.position, -Vector3.up, gameObject.GetComponent<Collider>().bounds.extents.y + 0.3f) == false)
{
moveSpeed = 9f;
jumping = true;
}
else
{
moveSpeed = 7f;
if (jumping == true)
{
jumping = false;
transform.GetChild(1).GetComponent<AudioSource>().Play();
gameObject.GetComponent<ParticleSystem>().Emit(new ParticleSystem.EmitParams(), 4);
StartCoroutine(jumpanimwait());
IEnumerator jumpanimwait()
{
yield return new WaitForSeconds(0.2f);
}
}
}
//weapon
if (Input.GetKeyDown(KeyCode.Mouse0))
{
StartCoroutine(gunwait());
IEnumerator gunwait()
{
smoothedweaponspeed = 0.5f;
smoothedvectorweapon = new Vector3(-0.021f,0.172f,-0.523f);
smoothedrotateweapon = Quaternion.Euler(0,0,33.318f);
transform.GetChild(2).GetChild(0).GetComponent<ParticleSystem>().Emit(new ParticleSystem.EmitParams(), 2);
GameObject clone = Instantiate(GameObject.Find("bullet"), transform.position + transform.GetChild(2).transform.right * 2 + -transform.GetChild(2).transform.forward * 0.5f, Quaternion.Euler(0,0,0), transform.parent);
clone.GetComponent<Rigidbody>().constraints = RigidbodyConstraints.None;
transform.GetChild(2).GetChild(0).GetComponent<AudioSource>().Play();
clone.transform.LookAt(clone.transform.position + transform.GetChild(2).transform.right * 5 + -transform.GetChild(2).transform.forward * 0.5f );
clone.GetComponent<Rigidbody>().velocity = transform.GetChild(2).transform.right * 50;
Destroy(clone, 2);
yield return new WaitForSeconds(0.1f);
smoothedweaponspeed = 0.05f;
smoothedrotateweapon = Quaternion.Euler(0,0,0);
smoothedvectorweapon = new Vector3(0.559f,0,-0.523f);
}
}
}
}
i got it, it was a mixture of programming more movement tech for my controller and generally messing around with physics materials and rigidbody mass, also interpolation was pretty important in the process
I'm trying to re-implement a pinch-to-zoom system in a Unity UI-based app. About six months ago I was able to hack one together by making the UI canvas a child of a regular GameObject, and manipulating that object's transform, but since updating to Unity 5.5+ I find this doesn't work. The closest I can get allows the pinch gesture to change the canvas' scaleFactor, which a) can make images, panels, etc resize improperly depending on their alignments, and b) won't allow me to pan once zoomed.
What I have so far is this:
public class PinchToZoomScaler : MonoBehaviour {
public Canvas canvas; // The canvas
public float zoomSpeed = 0.5f; // The rate of change of the canvas scale factor
public float _resetDuration = 3.0f;
float _durationTimer = 0.0f;
float _startScale = 0.0f;
void Start() {
_startScale = canvas.scaleFactor;
}
void Update()
{
// If there are two touches on the device...
if (Input.touchCount == 2) {
// Store both touches.
Touch touchZero = Input.GetTouch (0);
Touch touchOne = Input.GetTouch (1);
// Find the position in the previous frame of each touch.
Vector2 touchZeroPrevPos = touchZero.position - touchZero.deltaPosition;
Vector2 touchOnePrevPos = touchOne.position - touchOne.deltaPosition;
// Find the magnitude of the vector (the distance) between the touches in each frame.
float prevTouchDeltaMag = (touchZeroPrevPos - touchOnePrevPos).magnitude;
float touchDeltaMag = (touchZero.position - touchOne.position).magnitude;
// Find the difference in the distances between each frame.
float deltaMagnitudeDiff = prevTouchDeltaMag - touchDeltaMag;
// ... change the canvas size based on the change in distance between the touches.
canvas.scaleFactor -= deltaMagnitudeDiff * zoomSpeed;
// Make sure the canvas size never drops below 0.1
canvas.scaleFactor = Mathf.Max (canvas.scaleFactor, _startScale);
canvas.scaleFactor = Mathf.Min (canvas.scaleFactor, _startScale * 3.0f);
_durationTimer = 0.0f;
} else {
_durationTimer += Time.deltaTime;
if (_durationTimer >= _resetDuration) {
canvas.scaleFactor = _startScale;
}
}
}
}
As I said, this works to a degree, but doesn't give me a nice uniform zooming, not does it allow me to pan the canvas. Thanks in advance for any help.
Attach this script in canvas object which you want to zoom in and zoom out by pinch
using UnityEngine;
using UnityEngine.EventSystems;
public class ObjectScalling : MonoBehaviour, IPointerDownHandler, IPointerUpHandler
{
private bool _isDragging;
private float _currentScale;
public float minScale, maxScale;
private float _temp = 0;
private float _scalingRate = 2;
private void Start()
{
_currentScale = transform.localScale.x;
}
public void OnPointerDown(PointerEventData eventData)
{
if (Input.touchCount == 1)
{
_isDragging = true;
}
}
public void OnPointerUp(PointerEventData eventData)
{
_isDragging = false;
}
private void Update()
{
if (_isDragging)
if (Input.touchCount == 2)
{
transform.localScale = new Vector2(_currentScale, _currentScale);
float distance = Vector3.Distance(Input.GetTouch(0).position, Input.GetTouch(1).position);
if (_temp > distance)
{
if (_currentScale < minScale)
return;
_currentScale -= (Time.deltaTime) * _scalingRate;
}
else if (_temp < distance)
{
if (_currentScale > maxScale)
return;
_currentScale += (Time.deltaTime) * _scalingRate;
}
_temp = distance;
}
}
}
Reminder: This script only works in canvas objects
You can use this function ( just pass to it negative deltaMagnitudeDiff )
Also it is good to multiplay deltaMagnitudeDiff with a ratio like ( 0.05 )
float currentScale = 1f;
void Zoom (float increment)
{
currentScale += increment;
if (currentScale >= maxScale)
{
currentScale = maxScale;
}
else if (currentScale <= minScale)
{
currentScale = minScale;
}
rectTransform.localScale = new Vector3 (currentScale, currentScale, 1);
pan.ValidatePosition ();
}
For Panning,
you can use something like this :
public class Pan : MonoBehaviour
{
public float Speed;
Vector3 startDragPosition;
public void BeginDrag ()
{
startDragPosition = Input.mousePosition;
}
public void Drag ()
{
transform.localPosition += (Input.mousePosition - startDragPosition) * Speed;
startDragPosition = Input.mousePosition;
ValidatePosition ();
}
public void ValidatePosition ()
{
var temp = transform.localPosition;
var width = ((RectTransform)transform).sizeDelta.x;
var height = ((RectTransform)transform).sizeDelta.y;
var MaxX = 0.5f * width * Mathf.Max (0, transform.localScale.x - 1);
var MaxY = 0.5f * height * Mathf.Max (0, transform.localScale.y - 1);
var offsetX = transform.localScale.x * width * (((RectTransform)transform).pivot.x - 0.5f);
var offsetY = transform.localScale.y * width * (((RectTransform)transform).pivot.y - 0.5f);
if (temp.x < -MaxX + offsetX)
temp.x = -MaxX + offsetX;
else if (temp.x > MaxX + offsetX)
temp.x = MaxX + offsetX;
if (temp.y < -MaxY + offsetY)
temp.y = -MaxY + offsetY;
else if (temp.y > MaxY + offsetY)
temp.y = MaxY + offsetY;
transform.localPosition = temp;
}
Just call the functions ( BeginDrag & Drag ) from the Events Trigger component.
what i did to scale an object using pinch was this, it works on any touch screen when the object is in the middle of the screen:
if (Input.touchCount == 2)
{
//The distance between the 2 touches is checked and subsequently used to scale the
//object by moving the 2 fingers further, or closer form eachother.
Touch touch0 = Input.GetTouch(0);
Touch touch1 = Input.GetTouch(1);
if (isScaling)//this will only be done if scaling is true
{
float currentTouchDistance = getTouchDistance();
float deltaTouchDistance = currentTouchDistance - touchDistanceOrigin;
float scalePercentage = (deltaTouchDistance / 1200f) + 1f;
Vector3 scaleTemp = transform.localScale;
scaleTemp.x = scalePercentage * originalScale.x;
scaleTemp.y = scalePercentage * originalScale.y;
scaleTemp.z = scalePercentage * originalScale.z;
//to make the object snap to 100% a check is being done to see if the object scale is close to 100%,
//if it is the scale will be put back to 100% so it snaps to the normal scale.
//this is a quality of life feature, so its easy to get the original size of the object.
if (scaleTemp.x * 100 < 102 && scaleTemp.x * 100 > 98)
{
scaleTemp.x = 1;
scaleTemp.y = 1;
scaleTemp.z = 1;
}
//here we apply the calculation done above to actually make the object bigger/smaller.
transform.localScale = scaleTemp;
}
else
{
//if 2 fingers are touching the screen but isScaling is not true we are going to see if
//the middle of the screen is looking at the object and if it is set isScalinf to true;
Ray ray;
RaycastHit hitTouch;
ray = cam.ViewportPointToRay(new Vector3(0.5f, 0.5f, 0));
if (Physics.Raycast(ray, out hitTouch, 100f))
{
if (hitTouch.transform == transform)
{
isScaling = true;
//make sure that the distance between the fingers on initial contact is used as the original distance
touchDistanceOrigin = getTouchDistance();
originalScale = transform.localScale;
}
}
}
}
I am currently working on a project for one of the courses i am taking.
I am making a 2D game in unity3D, and i have a small problem, every time i run the game my character keeps on falling through the map, even though i have added a rigidbody2D and a boxCollider2D to both my character and the foreground. The code is attached, it is in C# and it is a bit long. Thank you so much in advance ..
enter code here
using System;
using UnityEngine;
using System.Collections;
public class CharacterController2D : MonoBehaviour
{
private const float SkinWidth = .02f;
private const int TotalHorizontalRays = 8;
private const int TotalVerticalRays = 4;
private static readonly float SlopeLimitTanget = Mathf.Tan (75f * Mathf.Deg2Rad);
public LayerMask PlatformMask;
public ControllerParameters2D DefaultParameters;
public ControllerState2D State { get; private set; }
public Vector2 Velocity { get { return _velocity; }}
public bool HandleCollisions { get; set; }
//Return overrideparamteres if it is not null, if it is null it will return DefaultParameters
public ControllerParameters2D Parameters { get { return _overrideParameters ?? DefaultParameters; } }
public GameObject StandingOn { get; private set;}
public Vector3 PlatformVelocity { get; private set;}
public bool CanJump
{
get
{
if(Parameters.JumpRestrictions == ControllerParameters2D.JumpBehavior.CanJumpAnywhere)
return _jumpIn <= 0;
if(Parameters.JumpRestrictions == ControllerParameters2D.JumpBehavior.CanJumpOnGround)
return State.IsGrounded;
return false;
}
}
private Vector2 _velocity;
private Transform _transform;
private Vector3 _localScale;
private BoxCollider2D _boxCollider;
private ControllerParameters2D _overrideParameters;
private float _jumpIn;
private GameObject _lastStandingOn;
private Vector3
_activeGlobalPlatformPoint,
_activeLocalPlatformPoint;
private Vector3
_raycastTopLeft,
_raycastBottomRight,
_raycastBottomLeft;
private float _verticalDistanceBetweenRays,
_horizonatalDistanceBetweenRays;
public void Awake()
{
HandleCollisions = true;
State = new ControllerState2D();
_transform = transform;
_localScale = transform.localScale;
_boxCollider = GetComponent <BoxCollider2D>();
// Absolute Value
var colliderWidth = _boxCollider.size.x * Mathf.Abs(transform.localScale.x) - (2 * SkinWidth);
_horizonatalDistanceBetweenRays = colliderWidth / (TotalVerticalRays - 1);
var colliderHeight = _boxCollider.size.y * Mathf.Abs( transform.localScale.y ) - (2 * SkinWidth);
_verticalDistanceBetweenRays = colliderHeight / (TotalHorizontalRays - 1);
}
public void AddForce(Vector2 force)
{
_velocity = force;
}
public void SetForce(Vector2 force)
{
_velocity += force;
}
public void SetHorizontalForce(float x)
{
_velocity.x = x;
}
public void SetVerticalForce(float y)
{
_velocity.y = y;
}
public void Jump()
{
AddForce(new Vector2(0, Parameters.JumpMagnitude));
_jumpIn = Parameters.JumpFrequency;
}
public void LateUpdate()
{
_jumpIn -= Time.deltaTime;
//We force the player to go up or down based on the gravity
_velocity.y += Parameters.Gravity * Time.deltaTime;
//Move the characther per his velocity scaled by time
Move (Velocity * Time.deltaTime);
}
// Ensures the player doesn't fall off the map or move through the wall
private void Move(Vector2 deltaMovement)
{
var wasGrounded = State.IsCollidingBelow;
State.Reset();
if(HandleCollisions)
{
HandlePlatforms();
CalculateRayOrigins();
if(deltaMovement.y < 0 && wasGrounded)
HandleVerticalSlope(ref deltaMovement);
if(Mathf.Abs(deltaMovement.x) > .001f)
MoveHorizontally(ref deltaMovement);
MoveVertically(ref deltaMovement);
CorrectHorizontalPlacement(ref deltaMovement, true);
CorrectHorizontalPlacement(ref deltaMovement, false);
}
_transform.Translate(deltaMovement, Space.World);
if (Time.deltaTime > 0)
_velocity = deltaMovement / Time.deltaTime;
_velocity.x = Mathf.Min (_velocity.x, Parameters.MaxVelocity.x);
_velocity.y = Mathf.Min (_velocity.y, Parameters.MaxVelocity.y);
if(State.IsMovingUpSlope)
_velocity.y = 0;
//Standing on the platform
if(StandingOn != null)
{
_activeGlobalPlatformPoint = transform.position;
_activeLocalPlatformPoint = StandingOn.transform.InverseTransformPoint(transform.position);
Debug.DrawLine(transform.position, _activeGlobalPlatformPoint);
Debug.DrawLine(transform.position, _activeLocalPlatformPoint + StandingOn.transform.position);
if(_lastStandingOn != StandingOn)
{
//If the last thing we are standing on is not null, send a message to leave it
if(_lastStandingOn != null)
_lastStandingOn.SendMessage("ControllerExist2D", this, SendMessageOptions.DontRequireReceiver);
//Inform what we are standing on that we have entered
StandingOn.SendMessage("ControllerEnter2D", this, SendMessageOptions.DontRequireReceiver);
_lastStandingOn = StandingOn;
}
//Invoke the platform that we are standing on it
else if (StandingOn != null)
StandingOn.SendMessage("ControllerStay2D", this, SendMessageOptions.DontRequireReceiver);
}
else if (_lastStandingOn != null)
{
_lastStandingOn.SendMessage("ControllerExit2D", this, SendMessageOptions.DontRequireReceiver);
_lastStandingOn = null;
}
}
private void HandlePlatforms()
{
//Calculate the velocity of the platform
if(StandingOn != null)
{
var newGlobalPlatformPoint = StandingOn.transform.TransformPoint(_activeLocalPlatformPoint);
var moveDistance = newGlobalPlatformPoint - _activeGlobalPlatformPoint;
//Sticks the player on the platform, wherever the platform teleport the players stays on it
if(moveDistance != Vector3.zero)
transform.Translate(moveDistance, Space.World);
PlatformVelocity = (newGlobalPlatformPoint - _activeGlobalPlatformPoint) / Time.deltaTime;
}
else
PlatformVelocity = Vector3.zero;
StandingOn = null;
}
private void CorrectHorizontalPlacement(ref Vector2 deltaMovement, bool isRight)
{
var halfwidth = (_boxCollider.size.x * _localScale.x) / 2f;
var rayOrigin = isRight ? _raycastBottomRight : _raycastBottomLeft;
if(isRight)
rayOrigin.x -= (halfwidth - SkinWidth);
else
rayOrigin.x += (halfwidth - SkinWidth);
var rayDirection = isRight ? Vector2.right : -Vector2.right;
var offset = 0f;
for(var i = 1; i <= TotalHorizontalRays - 1; i++)
{
var rayVector = new Vector2(deltaMovement.x + rayOrigin.x, deltaMovement.y + rayOrigin.y + (i * _verticalDistanceBetweenRays));
Debug.DrawRay(rayVector, rayDirection * halfwidth, isRight ? Color.cyan : Color.magenta);
var raycastHit = Physics2D.Raycast(rayVector, rayDirection, halfwidth, PlatformMask);
if(!raycastHit)
continue;
offset = isRight ? ((raycastHit.point.x - _transform.position.x) - halfwidth) : (halfwidth - (_transform.position.x - raycastHit.point.x));
}
deltaMovement.x += offset;
}
private void CalculateRayOrigins()
{
var size = new Vector2 (_boxCollider.size.x * Mathf.Abs (_localScale.x), _boxCollider.size.y * Mathf.Abs (_localScale.y)) / 2;
var center = new Vector2(_boxCollider.center.x * _localScale.x, _boxCollider.center.y * _localScale.y);
//Location of the player, then we add the box collider to it relative to the center of the player
_raycastTopLeft = _transform.position + new Vector3 (center.x - size.x + SkinWidth, center.y + size.y - SkinWidth);
_raycastBottomRight = _transform.position + new Vector3 (center.x + size.x - SkinWidth, center.y - size.y + SkinWidth); //Going right
_raycastBottomLeft = _transform.position + new Vector3 (center.x - size.x + SkinWidth, center.y - size.y + SkinWidth); //Going left and down-up
}
//Cast rays to the left or to the right depending on the player's movement
//Determining how far the player can go either to the left, or to the right
private void MoveHorizontally(ref Vector2 deltaMovement)
{
var isGoingRight = deltaMovement.x > 0;
//The distance between the starting point and the final destination
var rayDistance = Mathf.Abs (deltaMovement.x) + SkinWidth;
//Where is the player going? right or left
var rayDirection = isGoingRight ? Vector2.right : -Vector2.right;
//Right? we start from bottom right. Left? we start fro, bottom left
var rayOrigin = isGoingRight ? _raycastBottomRight : _raycastBottomLeft;
//Determines how many rays we want to shoot out to the left or to the right
for(var i = 0; i < TotalHorizontalRays; i++)
{
var rayVector = new Vector2(rayOrigin.x, rayOrigin.y + (i * _verticalDistanceBetweenRays));
//Visual representation about the rays
Debug.DrawRay(rayVector, rayDirection * rayDistance, Color.red);
//Checks if the player hit something or not
var rayCastHit = Physics2D.Raycast(rayVector, rayOrigin, rayDistance, PlatformMask);
if(!rayCastHit) //If there was a raycast then do something, otherwise continue to loop
continue;
//We return true if we are on a horizotnal slope, and check if we are going right or left or hit something while going up
if(i == 0 && HandleHorizontalSlope(ref deltaMovement, Vector2.Angle(rayCastHit.normal, Vector2.up), isGoingRight))
break;
//If we hit something then we can only go that far forward
deltaMovement.x = rayCastHit.point.x - rayVector.x;
rayDistance = Mathf.Abs(deltaMovement.x);
if(isGoingRight)
{
//If we are going right, then we have to substract the skinwidth
deltaMovement.x -= SkinWidth;
State.IsCollidingRight = true;
}
else
{
//The oppoiste of the if statement, if we are going left, we add the skinwidth
deltaMovement.x += SkinWidth;
State.IsCollidingLeft = true;
}
//Handles error collision, if the player hits something and go through it
if(rayDistance < SkinWidth + .0001f)
break;
}
}
private void MoveVertically(ref Vector2 deltaMovement)
{
//Check to see if going up or down
var isGoingUp = deltaMovement.y > 0;
var rayDistance = Mathf.Abs (deltaMovement.y) + SkinWidth;
var rayDirection = isGoingUp ? Vector2.up : -Vector2.up;
var rayOrigin = isGoingUp ? _raycastTopLeft : _raycastBottomLeft;
rayOrigin.x += deltaMovement.x;
var standingOnDistance = float.MaxValue;
for(var Count = 0; Count < TotalVerticalRays; Count++)
{
var rayVector = new Vector2(rayOrigin.x + (Count * _horizonatalDistanceBetweenRays), rayOrigin.y);
Debug.DrawRay(rayVector, rayDirection * rayDistance, Color.red);
var raycastHit = Physics2D.Raycast(rayVector, rayDirection, rayDistance, PlatformMask);
//If the player hit nothing then keep going.
if(raycastHit)
{
continue;
}
if(!isGoingUp)
{
var verticalDistanceToHit = _transform.position.y - raycastHit.point.y;
if(verticalDistanceToHit < standingOnDistance)
{
standingOnDistance = verticalDistanceToHit;
//Platform we are standing on
StandingOn = raycastHit.collider.gameObject;
}
}
//Determine the furthest distance we can move down or up without hitting anything
deltaMovement.y = raycastHit.point.y - rayVector.y;
rayDistance = Mathf.Abs(deltaMovement.y);
if(isGoingUp)
{
deltaMovement.y -= SkinWidth;
State.IsCollidingAbove = true;
}
else
{
deltaMovement.y += SkinWidth;
State.IsCollidingBelow = true;
}
if(!isGoingUp && deltaMovement.y > .0001f)
{
State.IsMovingUpSlope = true;
}
if(rayDistance < SkinWidth + .0001f)
{
break;
}
}
}
private void HandleVerticalSlope(ref Vector2 deltaMovement)
{
//Give us the center of the vertical rays;
var center = (_raycastBottomLeft.x + _raycastBottomRight.x) / 2;
var direction = -Vector2.up;
var slopeDistance = SlopeLimitTanget * (_raycastBottomRight.x - center);
var slopeRayVector = new Vector2 (center, _raycastBottomLeft.y);
Debug.DrawRay(slopeRayVector, direction * slopeDistance, Color.yellow);
var raycastHit = Physics2D.Raycast (slopeRayVector, direction, slopeDistance, PlatformMask);
if (!raycastHit)
return;
// ReSharper disable CompareOfFloatsByEqualityOperator
var isMovingDownSlope = Mathf.Sign (raycastHit.normal.x) == Mathf.Sign (deltaMovement.x);
if(!isMovingDownSlope)
return;
var angle = Vector2.Angle (raycastHit.normal, Vector2.up);
if(Mathf.Abs(angle) < .0001f)
return; //Which means there we are not on a slope, we are on something else
State.IsMovingDownSlope = true;
State.SlopeAngle = angle;
deltaMovement.y = raycastHit.point.y - slopeRayVector.y;
}
private bool HandleHorizontalSlope(ref Vector2 deltaMovement, float angle, bool isGoingRight)
{
//We do not want to move to an angle of 90
if(Mathf.RoundToInt(angle) == 90)
return false;
if(angle > Parameters.SlopeLimit)
{
deltaMovement.x = 0;
return true;
}
if(deltaMovement.y > .07f)
return true;
deltaMovement.x += isGoingRight ? -SkinWidth : SkinWidth;
deltaMovement.y = Mathf.Abs (Mathf.Tan (angle * Mathf.Deg2Rad) * deltaMovement.x);
State.IsMovingUpSlope = true;
State.IsCollidingBelow = true;
return true;
}
public void OnTriggerEnter2D(Collider2D other)
{
var parameters = other.gameObject.GetComponent<ControllerPhysicsVolume2D>();
if(parameters == null)
return;
_overrideParameters = parameters.Parameters;
}
public void OnTriggerExit2D(Collider2D other)
{
var parameters = other.gameObject.GetComponent<ControllerPhysicsVolume2D>();
if(parameters == null)
return;
_overrideParameters = null;
}
}
As #Terrance said you really don't need to write your own code logic for collision detection.
Secondly I noticed OnTriggerEnter2D and OnTriggerExit2D methods in your code, that also points out to one thing that all your boxCollider2d has isTrigger option checked. Therefore, I suggest you to uncheck isTrigger option on both your player and ground as TRIGGER will never stop two objects from crossing each other (you need to have both objects have 'isTrigger' unchecked on their colliders if you don't want them to pass through each other). And use method OnCollisionEnter2D and OnCollisionExit2D to detect the collision.
What is difference between Trigger and a Collider:
Taking real world example Colliders are tangible object e.g you yourself and the floor you are standing on both are solid and tangible.
While trigger is intangible, example of triggers can be the walkthrough security doors; these doors are hollow from inside allowing any person to pass without any hindrance, but if you wear any metal object on yourself and pass through that "Hollow Area" between the door, the door will TRIGGER an alarm. therefore with respect to the game world you can say that the door has a trigger at it hollow area.