MonoGame - Drag and Drop - monogame

I am currently writing a game using the monogame framework. I am having trouble with reacting to the touch input correctly. I want the user to be able to drag the "objToDrag". The problem is that both deltaX and deltaY are always zero. Here is my code:
var touchState = TouchPanel.GetState();
foreach (var touch in TouchPanel.GetState())
{
if (touch.State == TouchLocationState.Moved)
{
TouchLocation prevLoc;
if (!touch.TryGetPreviousLocation(out prevLoc)) continue;
float deltaX = touch.Position.X - prevLoc.Position.X;
float deltaY = touch.Position.Y - prevLoc.Position.Y;
this.objToDrag.X += deltaX;
this.objToDrag.Y += deltaY;
}
}

Use the delta value to calculate how far your object should move, no need to calculate your own.
foreach (var touch in TouchPanel.GetState())
{
if (touch.State == TouchLocationState.Moved)
{
TouchLocation prevLoc;
if (!touch.TryGetPreviousLocation(out prevLoc)) continue;
float deltaX = touch.Delta.X;
float deltaY = touch.Delta.Y;
this.objToDrag.X += deltaX;
this.objToDrag.Y += deltaY;
}
}

Related

How to make panel swiper for canvas that scales with screen size

I created a level menu manager where you swipe from one screen to the next horizontally, and it worked great. Until I realized I had to change the canvas to scale with screen size in order to preserve the layout for various devices. Now, the panels resize. Which is what I want, however it means the swipe no longer works since the panels stretch and thin to accommodate yet their position does not change. They might overlap now, or have big gaps in between. See photo examples. Is there anything I can do? I cannot for the life of me figure out how to calculate where their new position should be based on screen size and move them there using the left and right rect transform properties. It sounds easy but the math just does not math in all my efforts. Been tearing my hair out over this all day.
Code for my page swiper:
public class PageSwiper : MonoBehaviour, IDragHandler, IEndDragHandler
{
private Vector3 panelLocation;
public float percentThreshold = 0.2f;
public float easing = 0.5f;
int currentChild;
Player player;
void Start()
{
player = PlayerData.GetPlayerData();
currentChild = player.lastPanel;
panelLocation = transform.position;
panelLocation += new Vector3(-Screen.width * (currentChild-1), 0, 0);
transform.position = panelLocation;
}
public void OnDrag(PointerEventData data){
float difference = data.pressPosition.x - data.position.x;
if ((currentChild == 9 && difference < 0) || (currentChild == 1 && difference > 0))
{
transform.position = panelLocation - new Vector3(difference, 0, 0);
}
}
public void OnEndDrag(PointerEventData data){
float percentage = (data.pressPosition.x - data.position.x) / Screen.width;
if (Mathf.Abs(percentage) >= percentThreshold){
Vector3 newLocation = panelLocation;
int panelCount = transform.childCount - 1;
if (percentage > 0 && currentChild < panelCount){
newLocation += new Vector3(-Screen.width, 0, 0);
currentChild += 1;
}else if(percentage < 0 && currentChild > 1){
newLocation += new Vector3(Screen.width, 0, 0);
currentChild -= 1;
}
transform.position = newLocation;
panelLocation = newLocation;
} else {
transform.position = panelLocation;
}
}
}
Before, right next to each other for perfect swipe:
After example 1, wider screen size:
After example 2, thinner screen size:

How to prevent camera from going through specific layers

I have a camera controller that can move, zoom and rotate the camera. It's pretty much the exact camera controller from Cities Skyline. i.e top-down with some freedom of movement.
I am using CheckSphere to detect when the camera collides with wall/ground layers, and this works perfectly. However my issue is I do not know how to reset it's position to the last valid one, and to keep it there until the camera chooses a valid direction. At the moment as soon as my CheckSphere hits a wall, the camera is stuck and no longer responds to input.
I tried to store the last valid position, rotation and zoom and simply force this when CheckSphere hits something, but it's not working.
Is there some way I can can use this method with CheckSphere in a way that the camera won't get stuck every time it hits something it's not allowed to hit?
void HandleInput()
{
if (Physics.CheckSphere(cameraTransform.position, 7, collideWith))
{
newPosition = oldPosition;
newRotation = oldRotation;
newZoom = oldZoom;
Debug.Log("Did Hit");
}
else
{
//Speed controls
if (Input.GetKey(KeyCode.LeftShift))
{
movementSpeed = fastSpeed;
}
else
{
movementSpeed = normalSpeed;
}
// Adjust movement speed based on camera zoom
movementSpeed *= (cameraTransform.localPosition.y / zoomSpeedFactor);
Vector3 adjustedForward = transform.forward;
adjustedForward.y = 0;
//Movement controls
if (Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.UpArrow))
{
newPosition += (adjustedForward * movementSpeed);
}
if (Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.DownArrow))
{
newPosition += (adjustedForward * -movementSpeed);
}
if (Input.GetKey(KeyCode.D) || Input.GetKey(KeyCode.RightArrow))
{
newPosition += (transform.right * movementSpeed);
}
if (Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.LeftArrow))
{
newPosition += (transform.right * -movementSpeed);
}
//Rotation controls
if (Input.GetMouseButtonDown(2))
{
rotateStartPosition = Input.mousePosition;
}
if (Input.GetMouseButton(2))
{
rotateCurrentPosition = Input.mousePosition;
Vector3 difference = rotateStartPosition - rotateCurrentPosition;
rotateStartPosition = rotateCurrentPosition;
newRotation *= Quaternion.Euler(Vector3.up * (-difference.x / 5f));
newRotation *= Quaternion.Euler(Vector3.right * (difference.y / 5f));
Vector3 euler = newRotation.eulerAngles;
euler.x = ClampAngle(euler.x, verticalRotationClamp.x, verticalRotationClamp.y);
euler.z = 0;
newRotation.eulerAngles = euler;
}
//Zoom controls
if (Input.mouseScrollDelta.y != 0 && !EventSystem.current.IsPointerOverGameObject())
{
newZoom -= Input.mouseScrollDelta.y * zoomAmount;
newZoom.y = ClampValue(newZoom.y, zoomClamp.x, zoomClamp.y);
newZoom.z = ClampValue(newZoom.z, -zoomClamp.y, -zoomClamp.x);
}
oldPosition = new Vector3(newPosition.x, newPosition.y, newPosition.z);
oldRotation = new Quaternion(newRotation.x, newRotation.y, newRotation.z, newRotation.w);
oldZoom = new Vector3(newZoom.x, newZoom.y, newZoom.z);
Debug.Log("Did not Hit");
}
transform.position = Vector3.Lerp(transform.position, newPosition, Time.unscaledDeltaTime * acceleration);
transform.rotation = Quaternion.Lerp(transform.rotation, newRotation, Time.unscaledDeltaTime * acceleration);
cameraTransform.localPosition = Vector3.Lerp(cameraTransform.localPosition, newZoom, Time.unscaledDeltaTime * acceleration);
}

Unity Rotation Clamping Issue

I'm trying to implement a system where my camera will zoom in on the front face of a gameobject and I can then rotate around it with clamping.
I've managed to get it work, though I'm guessing I've done it totally wrong - Basically I'm expecting actualAngle to be NEGATIVE when I pan to the left and POSITIVE when I pan to the right.
It does work for targets that have no rotation but not for targets that do have a rotation as the centred angle is not zero.
Here's my function:
float _prevMousePosX;
void RotateCamera()
{
//Allow min -30 degrees when panning to the left
minXLimit = -50;
//And max 30 degrees when panning to the right
maxXLimit = 50;
//Work out how much the mouse has moved
var mouseX = Input.GetAxis("Mouse X");
Vector3 angle = target.position + target.rotation * transform.position * -1;
var actualAngle = angle.x;
//The angle is very small so times it by 100
actualAngle = actualAngle * 100;
//This angle is not defaulting to zero before any rotation has occurred when the game object has a rotation other than zero - I think I should be taking into account the existing rotation somehow
Debug.Log(actualAngle);
//The rest of this code is working correctly
if ((actualAngle > minXLimit && actualAngle < maxXLimit && isMoving))
{
//No - Allow rotating in either direction horizontally
if(isMoving) transform.RotateAround(target.transform.position, Vector3.up, mouseX * 1);
} else
{
//Yes, it's more than 30 degrees either left or right
//Work out which way wer're trying to drag the mouse and whether we should allow rotation further in that direction
if (mouseX>0 && actualAngle < minXLimit)
{
//Don't allow?
}
else if (mouseX<0 && actualAngle < minXLimit)
{
//Allow rotating back
if (isMoving) transform.RotateAround(target.transform.position, Vector3.up, mouseX * 1);
}
else
{
//Mouse isn't moving
}
if (mouseX>0 && actualAngle > maxXLimit)
{
//Allow rotating back
if (isMoving) transform.RotateAround(target.transform.position, Vector3.up, mouseX * 1);
}
else if(mouseX<0 && actualAngle > maxXLimit)
{
//Don't allow rotating in this direction any further
} else
{
//Mouse isn't moving
}
}
_prevMousePosX = mouseX;
}
Issue resolved.
//Work out how close we are to facing each other
//We can minus 180 because we'll always be facing each other, but just not always directly
var newAngle = Quaternion.Angle(target.rotation, transform.rotation) -180;
//Then we need to work out whether we are to it's left or it's right
float rotateDirection = (((transform.eulerAngles.y - target.eulerAngles.y) + 360f) % 360f) > 180.0f ? 1 : -1;
var actualAngle = newAngle;
//And finally we can negate our degrees if we're to it's left
actualAngle = actualAngle * rotateDirection;
You can then use actualAngle to clamp your rotation as I do above.

Unity: How to limit the zooming out of the camera

Using Unity 2018.3
This camera script allows the user to zoom in and out and move the camera with touch around.
On Start() the script takes the cameras current position and saves it as the out of bounds limit. So if the player is zoomed in and moves the camera to the side, the camera will stop when it reaches the bounds limit.
My problem is that currently the user can zoom out all they want, causing the camera to get shaky as the script is trying to relocate the camera but it doesn't stop the user from increasing the orthographicSize.
How do I limit the user from zooming out passed the out of bound limit it got on Start()?
public class ControlCamera : MonoBehaviour
{
private Vector2?[] oldTouchPositions = {
null,
null
};
private Vector2 oldTouchVector;
private float oldTouchDistance;
private Vector3 bottomLeft;
private Vector3 topRight;
private float cameraMaxY;
private float cameraMinY;
private float cameraMaxX;
private float cameraMinX;
public float maxZoom;
public float minZoom;
private void Start()
{
// Set max camera bounds
topRight = GetComponent<Camera>().ScreenToWorldPoint(new Vector3(GetComponent<Camera>().pixelWidth, GetComponent<Camera>().pixelHeight, -transform.position.z));
bottomLeft = GetComponent<Camera>().ScreenToWorldPoint(new Vector3(0, 0, -transform.position.z));
cameraMaxX = topRight.x;
cameraMaxY = topRight.y;
cameraMinX = bottomLeft.x;
cameraMinY = bottomLeft.y;
}
private void Update()
{
// No touches
if (Input.touchCount == 0)
{
oldTouchPositions[0] = null;
oldTouchPositions[1] = null;
}
// 1 Touch
else if (Input.touchCount == 1)
{
if (oldTouchPositions[0] == null || oldTouchPositions[1] != null)
{
oldTouchPositions[0] = Input.GetTouch(0).position;
oldTouchPositions[1] = null;
}
else
{
Vector2 newTouchPosition = Input.GetTouch(0).position;
transform.position += transform.TransformDirection((Vector3)((oldTouchPositions[0] - newTouchPosition) * GetComponent<Camera>().orthographicSize / GetComponent<Camera>().pixelHeight * 2f));
oldTouchPositions[0] = newTouchPosition;
}
// 2 touches or more
} else {
if (oldTouchPositions[1] == null)
{
oldTouchPositions[0] = Input.GetTouch(0).position;
oldTouchPositions[1] = Input.GetTouch(1).position;
oldTouchVector = (Vector2)(oldTouchPositions[0] - oldTouchPositions[1]);
oldTouchDistance = oldTouchVector.magnitude;
} else {
Vector2 screen = new Vector2(GetComponent<Camera>().pixelWidth, GetComponent<Camera>().pixelHeight);
Vector2[] newTouchPositions = {
Input.GetTouch(0).position,
Input.GetTouch(1).position
};
Vector2 newTouchVector = newTouchPositions[0] - newTouchPositions[1];
float newTouchDistance = newTouchVector.magnitude;
transform.position += transform.TransformDirection((Vector3)((oldTouchPositions[0] + oldTouchPositions[1] - screen) * GetComponent<Camera>().orthographicSize / screen.y));
GetComponent<Camera>().orthographicSize *= oldTouchDistance / newTouchDistance;
transform.position -= transform.TransformDirection((newTouchPositions[0] + newTouchPositions[1] - screen) * GetComponent<Camera>().orthographicSize / screen.y);
oldTouchPositions[0] = newTouchPositions[0];
oldTouchPositions[1] = newTouchPositions[1];
oldTouchVector = newTouchVector;
oldTouchDistance = newTouchDistance;
// --------------------------------------------------------------->>
// Control Zoom In
if (GetComponent<Camera>().orthographicSize > minZoom)
{
//
}
// Control Zoom Out
if (GetComponent<Camera>().orthographicSize < maxZoom)
{
//
}
// --------------------------------------------------------------->>
}
}
// Check if camera is out-of-bounds, if so, move back in-bounds
topRight = GetComponent<Camera>().ScreenToWorldPoint(new Vector3(GetComponent<Camera>().pixelWidth, GetComponent<Camera>().pixelHeight, -transform.position.z));
bottomLeft = GetComponent<Camera>().ScreenToWorldPoint(new Vector3(0, 0, -transform.position.z));
if (topRight.x > cameraMaxX)
{
transform.position = new Vector3(transform.position.x - (topRight.x - cameraMaxX), transform.position.y, transform.position.z);
}
if (topRight.y > cameraMaxY)
{
transform.position = new Vector3(transform.position.x, transform.position.y - (topRight.y - cameraMaxY), transform.position.z);
}
if (bottomLeft.x < cameraMinX)
{
transform.position = new Vector3(transform.position.x + (cameraMinX - bottomLeft.x), transform.position.y, transform.position.z);
}
if (bottomLeft.y < cameraMinY)
{
transform.position = new Vector3(transform.position.x, transform.position.y + (cameraMinY - bottomLeft.y), transform.position.z);
}
}
}
You should not use GetComponent repeatedly especially not in Update or any repeatedly called method as this creates a lot unnecessary overhead. Instead get it once
private Camera camera;
private void Awake()
{
camera = GetComponent<Camera>();
}
and reuse that reference -> Everywhere replace GetComponent<Camera>() width camera
If my understanding is correct, orthographicSize is half of the height of the camera in world units.
Since you already get topRight and bottomLeft in world coordinates you should be able to calculate an orthographicSize
maxZoom = (CameraMaxY - CameraMinY) / 2.0f;
however, if anyway you want the original zoom to be the max zoom you could also simply store the original zoom like
maxZoom = camera.orthographicSize;
And later you can simply clamp the value like
camera.orthographicSize = Mathf.Clamp(
camera.orthographicSize * oldTouchDistance / newTouchDistance,
minZoom,
maxZoom);
-> no need for extra if checks

Unity - How can I add or subtract a chosen number of degrees from an angle?

I was wondering if there's a way to add (in my case) 120 degrees everytime I push 'ButtonA', and subtract 120 degrees everytime I push 'ButtonB', from the Z-axis rotation of a 2d sprite (prefab).
This is the code I'm using at the moment, but it only rotates once left, and once right:
function TouchOnScreen ()
{
if (Input.touchCount > 0)
{
var touch = Input.touches[0];
if (touch.position.x < Screen.width/2)
{
var RSpeed = 10.0f
transform.rotation = Quaternion.Lerp ( transform.rotation,Quaternion.Euler(0,0,120), Time.deltaTime*RSpeed);
Debug.Log("RotateRight");
}
else if (touch.position.x > Screen.width/2)
{
var LSpeed = 10.0f
transform.rotation = Quaternion.Lerp ( transform.rotation,Quaternion.Euler(0,0,-120), Time.deltaTime*LSpeed);
Debug.Log("RotateLeft");
}
}
}
Thanks in advance!
Note: Please use unityscript if you can, I'm pretty new to coding and unityscript is all I know so far.
From the online documentation it is shown that the signature of the function is
static function Lerp(from: Quaternion, to: Quaternion, t: float): Quaternion;
that means the second parameter is the new rotation of the object not the rotation offset
you should use something of the sort
function TouchOnScreen ()
{
if (Input.touchCount > 0)
{
var touch = Input.touches[0];
if (touch.position.x < Screen.width/2)
{
var RSpeed = 10.0f
transform.rotation = Quaternion.Lerp ( transform.rotation,transform.rotation + Quaternion.Euler(0,0,120), Time.deltaTime*RSpeed);
Debug.Log("RotateRight");
}
else if (touch.position.x > Screen.width/2)
{
var LSpeed = 10.0f
transform.rotation = Quaternion.Lerp ( transform.rotation,transform.rotation + Quaternion.Euler(0,0,-120), Time.deltaTime*LSpeed);
Debug.Log("RotateLeft");
}
}
}
note the second parameter is transform.rotation + Quaternion.Euler(0,0,120) (current rotation + offset to the right)
I'm not an expert on unity engine (I only started toying with the free version yesterday, literally)
Try this one
function TouchOnScreen ()
{
if (Input.touchCount > 0)
{
var touch = Input.touches[0];
if (touch.position.x < Screen.width/2)
{
var RSpeed = 10.0f
transform.Rotate(0,0,120);
Debug.Log("RotateRight");
}
else if (touch.position.x > Screen.width/2)
{
var LSpeed = 10.0f
transform.Rotate(0,0,-120);
Debug.Log("RotateLeft");
}
}
}
If this not works try with Gameobject. I didn't check this