My level selection menu scene will be a graphic map. Think level selection similar to candy crush. I have different sprites for different maps that will be instantiated when the user moves the camera to that map. In order for me to achieve this, I scroll/move the camera using touch on it's Y-axis. But the problem is, I'm having a trouble getting the boundary of the map sprite, I already read a lot of solution about this one. Here is my current script. I don't know what I'm doing wrong. My camera got stuck when it is near the boundary. Thank you for your help
public float scrollSpeed = 10.0f;
public GameObject[] maps;
Camera mainCamera;
private float topBound;
private float bottomBound;
private Vector3 pos;
private SpriteRenderer currentSprite;
private int mapIndex;
void Awake ()
{
mapIndex = 0;
mainCamera = Camera.main;
currentSprite = maps [mapIndex].GetComponent<SpriteRenderer> ();
bottomBound = currentSprite.sprite.bounds.size.y * -1;
topBound = currentSprite.sprite.bounds.size.y + currentSprite.gameObject.transform.position.y;
}
void Update ()
{
if (Input.touchCount > 0 && Input.GetTouch (0).phase == TouchPhase.Moved)
{
Vector2 touchDeltaPosition = Input.GetTouch(0).deltaPosition;
mainCamera.transform.Translate(0f,-touchDeltaPosition.y * scrollSpeed * Time.deltaTime,0f);
}
}
private float getAxisDelta(float axisDelta,float camMin,float camMax,float bottom,float top)
{
//get where edge of camera will have moved if full translate is done
float boundaryMinPixelsDestination = camMin - Mathf.Abs(axisDelta);
float boundaryMaxPixelsDestination = camMax + Mathf.Abs(axisDelta);
Debug.Log("MaxPixels:"+boundaryMaxPixelsDestination+"="+camMax+"+"+Mathf.Abs(axisDelta));
//check to see if you're within the border
if ((boundaryMinPixelsDestination <= bottom && axisDelta > 0) ||
(boundaryMaxPixelsDestination >= top && axisDelta < 0))
{
axisDelta = 0;
}
return axisDelta;
}
private float camBoundPos(string pos)
{
float bounds = 0f;
if (pos == "top")
bounds = mainCamera.transform.position.y + mainCamera.orthographicSize;
if(pos == "bottom")
bounds = mainCamera.transform.position.y - mainCamera.orthographicSize;
return bounds;
}
Related
For the past 3 days, I have tried numerous methods and read dozens of questions on forums in order to achieve the desired effect of rotating a camera on the Y axis using mouse rotations, and clamping the rotation within a certain range. The amount of rotation is dependent on how close the mouse is to the edge of the screen.
I was unable to find a solution, but I was able to learn enough to have my own honest attempt at it, and I came quite close. The prominent issue I faced was being able to properly clamp the RotateAround() inside of the desired range. This is because of eulerAngles being 0 - 360, and having the min and max of the clamp transition to the other side of the spectrum.
By clamping the rotation degrees before calling RotateAround, I was able to get the clamping to work, though I had to use Booleans to get them to work properly.
My current issue is that when the initial Y rotation(anchor) is slightly below 360, the camera fails to clamp on the left and will clamp back around to the right side of the range.
In all other situations, the clamping works just fine:
Initial Y rotation slightly above 0
Not within the dead zone
While debugging, I find that this is because rightOverFlag is true. When I set this bool to true manually in situations clamping normally works fine, the clamping will no longer work on the left.
I can't seem to find how this bool being true causes something like this to happen, so I am hoping some fresh eyes and seasoned advice can help me learn from this. Thank you.
public gameState gameState;
public openCams openCams;
private GameObject GameStateManager;
[SerializeField]
private GameObject Player;
[SerializeField]
private Camera cam;
// Rotation Variables
private Quaternion initialRotation;
private float initialYRotation = 0f;
[SerializeField]
private float angleRange;
private float anchorY = 0f;
public bool leftOverFlag = false;
public bool rightOverFlag = false;
void Start()
{
cam = Camera.main;
Cursor.lockState = CursorLockMode.None;
}
public void OrientControls()
{
initialRotation = Player.transform.rotation;
initialYRotation = Player.transform.eulerAngles.y;
anchorY = initialYRotation;
if (anchorY > 360)
{
anchorY -= 360;
}
rightOverFlag = false;
leftOverFlag = false;
if ((anchorY + angleRange) > 360)
{
rightOverFlag = true;
}
else if ((anchorY - angleRange) <= 0)
{
leftOverFlag = true;
}
}
void Update()
{
if (openCams.GetMonitorState() == false)
{
mousePos = Input.mousePosition;
float rotateDegrees = 0f;
//Debug.Log(Player.transform.eulerAngles.y);
// THERE IS CODE HERE THAT INCREASES ROTATE DEGREES BASED ON MOUSE POSITION. I removed it for the sake of readability, because it was quite long and unrelated to my issue.
float angleFromInitial = Quaternion.Angle(initialRotation, Player.transform.rotation);
float currentPlayerYRot = Player.transform.eulerAngles.y;
if (currentPlayerYRot > 360)
{
currentPlayerYRot -= 360;
}
bool currentRightOverageFlag = false;
bool currentLeftOverageFlag = false;
if (rightOverFlag)
{
if ((currentPlayerYRot) < (anchorY - (angleRange - 5))) //
{
currentRightOverageFlag = true;
}
}
if (leftOverFlag)
{
if ((currentPlayerYRot) > (anchorY + (angleRange + 5)))
{
currentLeftOverageFlag = true;
}
}
// !!! - For some reason, when rightOverFlag is enabled, the clamp does not work on the left side. In all other situations, the clamp works perfectly.- !!!
if (!currentLeftOverageFlag && !currentRightOverageFlag)
{
if (currentPlayerYRot < anchorY) // Regular
{
angleFromInitial *= -1;
}
}
else if (currentLeftOverageFlag && !currentRightOverageFlag)
{
if (currentPlayerYRot > anchorY) // If over the left line
{
angleFromInitial *= -1;
}
}
else if (!currentLeftOverageFlag && currentRightOverageFlag)
{
if (currentPlayerYRot > anchorY) // If over the right line
{
angleFromInitial *= -1;
}
}
else
{
Debug.Log("staticPersonController: ERROR => Cannot have current left and right overage flags enabled at the same time.");
}
currentLeftOverageFlag = false;
currentRightOverageFlag = false;
float newAngle = Mathf.Clamp(angleFromInitial + rotateDegrees, -angleRange, angleRange);
rotateDegrees = newAngle - angleFromInitial;
Player.transform.RotateAround(Player.transform.position, Vector3.up, rotateDegrees);
}
}
}
Assume you have these two variables set.
// This is the initial value of the player's Y rotation. Set only once.
float initialRotY = Player.transform.eulerAngles.y;
// This is the offset of the player's Y rotation based on the cursor position
// e.g. -90 when the cursor is on the left edge and 90 on the right
float mouseBasedRotY;
In the Update method, just do this.
Player.transform.rotation = Quaternion.Euler(0f, initialRotY + mouseBasedRotY, 0f);
You don't have to clamp the value, because the degree is always limited in [initialRotY-90, initialRotY+90]
i did a tutorial about doing a camera drag on a 2D or 3D worldmap.
The code is working. Looks like the following:
void Update_CameraDrag ()
{
if( Input.GetMouseButtonUp(0) )
{
Debug.Log("Cancelling camera drag.");
CancelUpdateFunc();
return;
}
// Right now, all we need are camera controls
Vector3 hitPos = MouseToGroundPlane(Input.mousePosition);
//float maxWidth = 10;
//if (hitPos.x > maxWidth)
//{
// hitPos.x = maxWidth;
//}
Vector3 diff = lastMouseGroundPlanePosition - hitPos;
Debug.Log(diff.x+ Space.World);
Camera.main.transform.Translate (diff, Space.World);
lastMouseGroundPlanePosition = hitPos = MouseToGroundPlane(Input.mousePosition);
}
Now my problem is, that you can drag unlimiited into any directions.
I would rather like to difine something like borders on the x and y axis.
Basically maximum values for the camera. If the camera would surpass these values, their position value would be set to the given maximum value.
Unfortunately, i am not sure how it works, especially, since the tutorial set everything into relation to Space.World - and i am not even sure what that is. I mean i understand that "diff" is the change between the current position and the new positon in relation to Space.World and then the camera gets moved accordingly. I would just like to define a max value which the camera can not surpass. Any ideas how to do that. I am unfortunately still learning - so kinda hard for me and i was hoping for help.
If you were to record the X and Y position of the camera as it goes in a variable and use the MathF function. I.e
if you have a map that is 100(x)x150(y)units you could use
xPositionOfCamera = Mathf.Clamp(xPositionOfCamera, -50, 50);
yPositionOfCamera = Mathf.Clamp(YPositionOfCamera, -75, 75);
I'm not 100% sure if that's what you want it to do, but it's how I would limit it.
I write a simple and reliable script for my game to handle camera drag and swipe for any camera aspect ratio. Everyone can use this code easily :) Just adjust the xBoundWorld and yBoundWorld values
using UnityEngine;
public class CameraDragController : MonoBehaviour
{
[SerializeField] private Vector2 xBoundWorld;
[SerializeField] private Vector2 yBoundWorld;
[SerializeField] public bool HorizentalDrag = true;
[SerializeField] public bool VerticalDrag = true;
[SerializeField] public float speedFactor = 10;
private float leftLimit;
private float rightLimit;
private float topLimit;
private float downLimit;
public bool allowDrag = true;
private void Start()
{
CalculateLimitsBasedOnAspectRatio();
}
public void UpdateBounds(Vector2 xBoundNew, Vector2 yBoundNew)
{
xBoundWorld = xBoundNew;
yBoundWorld = yBoundNew;
CalculateLimitsBasedOnAspectRatio();
}
private void CalculateLimitsBasedOnAspectRatio()
{
leftLimit = xBoundWorld.x - Camera.main.ViewportToWorldPoint(new Vector3(0, 0, 0)).x;
rightLimit = xBoundWorld.y - Camera.main.ViewportToWorldPoint(new Vector3(1, 0, 0)).x;
downLimit = yBoundWorld.x - Camera.main.ViewportToWorldPoint(new Vector3(0, 0, 0)).y;
topLimit = yBoundWorld.y - Camera.main.ViewportToWorldPoint(new Vector3(0, 1, 0)).y;
}
Vector3 lastPosView; // we use viewport because we don't want devices pixel density affect our swipe speed
private void LateUpdate()
{
if (allowDrag)
{
if (Input.GetMouseButtonDown(0))
{
lastPosView = Camera.main.ScreenToViewportPoint(Input.mousePosition);
}
else if (Input.GetMouseButton(0))
{
var newPosView = Camera.main.ScreenToViewportPoint(Input.mousePosition);
var cameraMovment = (lastPosView - newPosView) * speedFactor;
lastPosView = newPosView;
cameraMovment = Limit2Bound(cameraMovment);
if (HorizentalDrag)
Camera.main.transform.Translate(new Vector3(cameraMovment.x, 0, 0));
if (VerticalDrag)
Camera.main.transform.Translate(new Vector3(0, cameraMovment.y, 0));
}
}
}
private Vector3 Limit2Bound(Vector3 distanceView)
{
if (distanceView.x < 0) // Check left limit
{
if (Camera.main.transform.position.x + distanceView.x < leftLimit)
{
distanceView.x = leftLimit - Camera.main.transform.position.x;
}
}
else // Check right limit
{
if (Camera.main.transform.position.x + distanceView.x > rightLimit)
{
distanceView.x = rightLimit - Camera.main.transform.position.x;
}
}
if (distanceView.y < 0) // Check down limit
{
if (Camera.main.transform.position.y + distanceView.y < downLimit)
{
distanceView.y = downLimit - Camera.main.transform.position.y;
}
}
else // Check top limit
{
if (Camera.main.transform.position.y + distanceView.y > topLimit)
{
distanceView.y = topLimit - Camera.main.transform.position.y;
}
}
return distanceView;
}
}
I'm currently trying to snap objects (cubes, pyramids, torus) together. I assigned each face of the objects bounding box a "snap allowed" variable and on collision try to find the closest two sides to each other. I then move the main object to the collision side and rotate it accordingly.
The code is as follows:
private static Vector3[] sides = {
Vector3.up,
Vector3.forward,
Vector3.right,
Vector3.down,
Vector3.back,
Vector3.left
};
private void Snap() {
if (!snappedObject)
return;
if (lastSnapping > LeftController.GetLastSnappingToggle()) {
//we dont calculate new sides, only use the old ones
//but we have to check whether we are now further away
float dis = Vector3.Distance(GetWorldPositionFace(this.gameObject, lastSelf),
GetWorldPositionFace(snappedObject, lastOther));
float max = Mathf.Max(Mathf.Abs(size.x), Mathf.Max(Mathf.Abs(size.y), Mathf.Abs(size.z)));
if (dis > max)
return;
ApplyToOther(lastSelf, lastOther, snappedObject);
} else {
//we need to find both new closest sides
MeshSnapping other = snappedObject.GetComponent<MeshSnapping>();
float otherDis = float.MaxValue;
int otherSide = -1;
//find the closest side from the other object
for (int i = 0; i < NUM_SIDES; i++) {
float dis = Vector3.Distance(transform.position, GetWorldPositionFace(snappedObject, i));
if (dis < otherDis && other.sidesAllowed[i]) {
otherDis = dis;
otherSide = i;
}
}
//find the closest side of our object
float selfDis = float.MaxValue;
int selfSide = 0;
for (int i = 0; i < NUM_SIDES; i++) {
float dis = Vector3.Distance(GetWorldPositionFace(this.gameObject, i),
GetWorldPositionFace(snappedObject, otherSide));
if (dis < selfDis && sidesAllowed[i]) {
selfDis = dis;
selfSide = i;
}
}
//are we to far away or at a prohibited side?
float max = Mathf.Max(Mathf.Abs(size.x), Mathf.Max(Mathf.Abs(size.y), Mathf.Abs(size.z)));
if (selfDis > max)
return;
ApplyToOther(selfSide, otherSide, snappedObject);
//save the sides for next iteration
lastSelf = selfSide;
lastOther = otherSide;
}
lastSnapping = Time.time;
}
private void OnCollisionEnter(Collision collision) {
snappedObject = collision.gameObject;
}
private void OnCollisionExit(Collision collision) {
snappedObject = null;
}
private Vector3 GetWorldPositionFace(GameObject other, int i) {
//get the side in local coordinates, scaled to size
Vector3 otherLocalSize = other.transform.localScale;
Vector3 otherSidePoint = new Vector3(otherLocalSize.x * sides[i].x, otherLocalSize.y * sides[i].y, otherLocalSize.z * sides[i].z) / 2f;
//rotate it according to world position
Vector3 dir = (other.transform.rotation * otherSidePoint);
//actually move it to world position
Vector3 center = other.transform.position + dir;
return center;
}
private void ApplyToOther(int selfI, int otherI, GameObject other) {
//first get the midpoint of face of other go
Vector3 edge = GetWorldPositionFace(other, otherI);
Vector3 dir = edge - other.transform.position;
RotateSides(selfI, otherI, dir);
selfI = (selfI + NUM_SIDES / 2) % NUM_SIDES;
//get midpoint of face of self go
edge += GetWorldPositionFace(this.gameObject, selfI) - transform.position;
//now move towards the combination
transform.position = edge;
}
private void RotateSides(int selfI, int otherI, Vector3 dir) {
//rotate self side towards this point
switch (selfI) {
case 0: transform.up = -dir; break;
case 1: transform.forward = -dir; break;
case 2: transform.right = -dir; break;
case 3: transform.up = dir; break;
case 4: transform.forward = dir; break;
case 5: transform.right = dir; break;
}
}
I can find every midpoint of the bounding box by transforming the direction vector, applying the objects current rotation and position to it (see GetWorldPositionFace() ). After finding the best combination, ApplyToOther() moves the objects to position and rotates it according to the selected face normals. So far so good, but the result is not aligned. As you can see, the front faces do not face in the same direction, i.e. I want to rotate the upper cube around the transform.up axis by this amount. This would be the result that I want.
But, if I add
float angle = Vector3.Angle(transform.forward, snappedObject.transform.forward);
transform.Rotate(transform.up, angle);
to the RotateSides() function, the result is this. The rotation axis is wrong.
Using
Quaternion.FromToRotation(transform.up, snappedObject.transform.up)
did not work either.
What did I miss? Thanks for your help!
I figured out my problem. By setting the transform.forward and the transform.up seperately (e.g. with transform.rotate around axis), only one of them was correct. Using Quaternion.LookRotation() solves this.
i'm trying to implement a simple orbit camea that can rotate and zoom in a scene. I followed some tutorials and i can run my program without any problems on windows if i build it as a PC Standalone. If i build it as WebGL my camera still correctly works with a single touch (rotation) , with multi touch i have a strange behaviour.. Difficult to explain but it zoomsin and out even if i do not move the fingers. Is king of "fuzzy". It happens with Firefox and Chrome, the browsers i have tested.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraOrbitTouch : MonoBehaviour {
protected Transform _XForm_Camera;
protected Transform _XForm_Parent;
protected Vector3 _LocalRotation;
protected float _CameraDistance = 10f;
// The rate of change of the field of view in perspective mode.
public float perspectiveZoomSpeed = 0.2f;
public float OrbitDampening = 30f;
public float ScrollDampening = 18f;
public bool CameraDisabled = false;
// Use this for initialization
void Start() {
this._XForm_Camera = this.transform;
this._XForm_Parent = this.transform.parent;
}
void LateUpdate() {
if (Input.GetKeyDown(KeyCode.LeftShift))
CameraDisabled = !CameraDisabled;
if (!CameraDisabled)
{
//Rotation of the Camera based on Mouse Coordinates
if (Input.touchCount == 1 && Input.GetTouch(0).phase == TouchPhase.Moved)
{
_LocalRotation.x += Input.touches[0].deltaPosition.x ;
_LocalRotation.y += Input.touches[0].deltaPosition.y;
//Clamp the y Rotation to horizon and not flipping over at the top
if (_LocalRotation.y < 0f)
_LocalRotation.y = 0f;
else if (_LocalRotation.y > 90f)
_LocalRotation.y = 90f;
}
//Zooming Input from our Mouse Scroll Wheel
if (Input.touchCount == 2 && Input.GetTouch(0).phase == TouchPhase.Moved && Input.GetTouch(1).phase == TouchPhase.Moved)
{
// 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);
//this._CameraDistance += deltaMagnitudeDiff * -1f;
this._CameraDistance += deltaMagnitudeDiff * perspectiveZoomSpeed;
this._CameraDistance = Mathf.Clamp(this._CameraDistance, 1.5f, 100f);
}
}
//Actual Camera Rig Transformations
Quaternion QT = Quaternion.Euler(_LocalRotation.y, _LocalRotation.x, 0);
this._XForm_Parent.rotation = Quaternion.Lerp(this._XForm_Parent.rotation, QT, Time.deltaTime * OrbitDampening);
if ( this._XForm_Camera.localPosition.z != this._CameraDistance * -1f )
{
this._XForm_Camera.localPosition = new Vector3(0f, 0f, Mathf.Lerp(this._XForm_Camera.localPosition.z, this._CameraDistance * -1f, Time.deltaTime * ScrollDampening));
}
}
}
Can someone can tell me why this is happening? Have my code some errors? There is some kind of incompatibility with WebGL and multi touch? Thans for your time and answers
Ok i found the problem. The problem was Windows 7. I used the same code on another computer with windows 10 and it works as intended...
I am creating a 2D platform / adventure game that uses flip screen to manage changing rooms.
My character moves using RigidBody.velocity, this works as I need it to, when my character reaches one of the screen's left or right extremes I use transform.position to move (effectively teleporting) the character to the other side of the screen, I then change the room. This gives the effect of moving to the next room along.
My flip screen code works fine, the changing room effect works without a problem, in fact almost everything works just as I like it, except that I get 1 frame where the character appears at a random point in between where the character moved from and where he moved to.
For example, my room is 16 tiles across, if my character moves left and passes x position '0' (the very left of the screen) I make a transform.position to x position 16 (keeping the y position constant) and then change rooms. I have logged the movement and what happens is (pseudo code)..
x < 0 (the change room code is run)
translate.position to x=16 (the far right)
x = 16 (this is correct)
The room changes
x = some float somewhere between 0 and 16 (this is NOT correct)
x = 16 (back to the correct position again)
This is my player code:
using UnityEngine;
using System.Collections;
public class MKManager : MonoBehaviour {
public float maxSpeed = 0.10f;
bool isFacingRight = false;
Rigidbody2D myBody;
bool grounded = true;
public static int liftdirection = 0;
void Start (){
myBody = GetComponent<Rigidbody2D> ();
}
void Update () {
checkRoomBounds ();
}
void FixedUpdate(){
float h = Input.GetAxisRaw ("Horizontal");
if (grounded) {
myBody.velocity = new Vector2 (h * maxSpeed, myBody.velocity.y);
if (h < 0) { //Moving Left
if (isFacingRight) {
FlipAnimation ();
}
} else if (h > 0) { //Moving Right
if (!isFacingRight) {
FlipAnimation ();
}
}
}
}
void checkRoomBounds(){
if (transform.position.x < 0) {
transform.position = new Vector3 (16.0f, transform.position.y, 0.0f);
GameManager.instance.moveRoom (-1);
} else if (transform.position.x > 16) {
transform.position = new Vector3 (0.0f, transform.position.y, 0.0f);
GameManager.instance.moveRoom (1);
}
}
void FlipAnimation(){
isFacingRight = !isFacingRight;
Vector3 theScale = transform.localScale;
theScale.x *= -1;
transform.localScale = theScale;
}
I have tried calling checkRoomBounds() in update, fixed update and lateupdate, all give the same result. If I do not call the moveRoom() function the same happens but the room is not changed, but the moveRoom() code is...
public void moveRoom(int direction){
if (room == 0) {
room = (10*currentFloor) + 1;
} else {
room += direction;
if (room == -1) {
room = 68;
} else if (room == 69) {
room = 0;
} else if (room % 10 == 9 || room % 10 == 0) {
//room += (direction * 2);
room = 0;
}
}
boardScript.SetupScene (room);
}
I've run out of ideas and don't know where I'm going wrong. Any help would be much appreciated.
Many thanks in advance