i wanted to ask you if there is a Resolution Scale Option like in Unreal Engine in Unity too. I have looked around the internet but didn't found anything.
If you are wanting to scale objects that are not on the canvas based on the resolution then offhand I can't think of anything. However, it would be fairly easy to implement something that does this.
Create a Script and attach it to every object that should scale based on the current resolution.
public class ScaleObjectFromRes : MonoBehaviour
{
private Vector2 targetResoultion = new Vector2(1920, 1080); //can be changed here or elsewhere
private bool matchWidth = true; //0=width, 1=height used to maintain aspect ratio
// Start is called before the first frame update
void Start()
{
float difference = CalculateDifference();
ScaleObj(difference);
}
void ScaleObj(float diff)
{
gameObject.transform.localScale += (gameObject.transform.localScale * (diff/100));
}
private float CalculateDifference()
{
Vector2 actualResolution = new Vector2(Screen.width, Screen.height);
Vector2 change = actualResolution-targetResoultion;
Vector2 percentChange = (change / targetResoultion) * 100;
//match width/height
if (matchWidth)
{
return percentChange.x;
}
else
{
return percentChange.y;
}
}
This scales the object based on the percent difference between the target resolution and the actual resolution. We can choose to match the difference based upon the width or height to guarantee a constant ratio for the object. This assumes the object's scale vector's magnitude is 1. Hope this helps!
Related
I'm adding the option for players to move the camera to the sides. I also want to limit how far they can move the camera to the sides.
If the camera was aligned with the axis, I could simply move around X/Z axis and set a limit on each axis as to how far it can go. But my problem is that the camera is rotated, so I'm stuck figuring out how to move it and set a limit. How could I implement this?
using UnityEngine;
[RequireComponent(typeof(Camera))]
public class CameraController : MonoBehaviour
{
Camera cam;
Vector3 dragOrigin;
bool drag = false;
void Awake()
{
cam = GetComponent<Camera>();
}
void LateUpdate()
{
// Camera movement with mouse
Vector3 diff = (cam.ScreenToWorldPoint(Input.mousePosition)) - cam.transform.position;
if (Input.GetMouseButton(0))
{
if (drag == false)
{
drag = true;
dragOrigin = cam.ScreenToWorldPoint(Input.mousePosition);
}
}
else
{
drag = false;
}
if (drag)
{
// Here I want to set a constraint in a rectangular plane perpendicular to camera view
transform.position = dragOrigin - diff;
}
}
}
Transform in Unity comes with a handy Transform.right property, which regards the object's rotation. To move your camera sideways you could further utilize Lerp to make the movement smooth.
transform.position += transform.right * factor
moves an object to the right.
Use factor to adjust the desired distance and by doing so you can also set limits. Negative factor would mean moving left by the way:) Hope that helps!
It can be tricky to deal with constraints on rotated objects. The math behind this includes some vector/rotation math to figure out the correct limits relative to the object's orientation, and whether you've exceeded them.
Luckily though, Unity gives you some shortcuts to skip this math: Transform.InverseTransformPoint() and Transform.TransformPoint()! These two methods allow you to transform a point in world space into a point in local space, and vice versa.
That means that no matter how your camera is oriented, you can interpret a position from the orientation of the camera - and with just a couple extra steps, your X/Z constraints are usable because you can calculate X/Z from the camera's point of view.
Let's try to adapt your current script to use this:
using UnityEngine;
[RequireComponent(typeof(Camera))]
public class CameraController : MonoBehaviour
{
// Set the X and Z values in the editor to define the rectangle within
// which your camera can move
public Vector3 maxConstraints;
public Vector3 minConstraints;
Camera cam;
Vector3 dragOrigin;
bool drag = false;
Vector3 cameraStart;
void Awake()
{
cam = GetComponent<Camera>();
// Here, we record the start since we'll need a reference to determine
// how far the camera has moved within the allowed rectangle
cameraStart = transform.position;
}
void LateUpdate()
{
// Camera movement with mouse
Vector3 diff = (cam.ScreenToWorldPoint(Input.mousePosition)) - cam.transform.position;
if (Input.GetMouseButton(0))
{
if (drag == false)
{
drag = true;
dragOrigin = cam.ScreenToWorldPoint(Input.mousePosition);
}
}
else
{
drag = false;
}
if (drag)
{
// Now, rather than setting the position directly, let's make sure it's
// within the valid rectangle first
Vector3 newPosition = dragOrigin - diff;
// First, we get into the local space of the camera and determine the delta
// between the start and possible new position
Vector3 localStart = transform.InverseTransformPoint(cameraStart);
Vector3 localNewPosition = transform.InverseTransformPoint(newPosition);
Vector3 localDelta = localNewPosition - localStart;
// Now, we calculate constrained values for the X and Z coordinates
float clampedDeltaX = Mathf.Clamp(localDelta.x, minConstraint.x, maxConstraint.x);
float clampedDeltaZ = Mathf.Clamp(localDelta.z, minConstraint.z, maxConstraint.z);
// Then, we can use the constrained values to determine the constrained position
// within local space
Vector3 localClampedPosition = new Vector3(clampedDeltaX, localDelta.y, clampedDeltaZ)
+ localStart;
// Finally, we can convert the local position back to world space and use it
transform.position = transform.TransformPoint(localConstrainedPosition);
}
}
}
Note that I'm somewhat assuming dragOrigin - diff moves your camera correctly in its present state. If it doesn't do what you want, please include details on the unwanted behaviour and we can sort that out too.
In Unity i have a UI Panel which has a player object (an UI Image object).
I moving player object into planel with user inputs (keyboard or touch)
I can't keep player object in it's parent panel,
Please check below image, I want to keep player inside of Red Panel
Here is my Tried Code
public Camera MainCamera; //be sure to assign this in the inspector to your main camera
private Vector2 screenBounds;
private float objectWidth;
private float objectHeight;
private RectTransform pnlBackgroundTransform;
private void Start()
{
pnlBackgroundTransform = GameObject.Find("PnlBackground").GetComponent<RectTransform>();
screenBounds = MainCamera.ScreenToWorldPoint(new Vector3(pnlBackgroundTransform.rect.width , pnlBackgroundTransform.rect.height , MainCamera.transform.position.z));
objectWidth = transform.GetComponent<SpriteRenderer>().bounds.extents.x; //extents = size of width / 2
objectHeight = transform.GetComponent<SpriteRenderer>().bounds.extents.y; //extents = size of height / 2
}
void LateUpdate()
{
Vector3 viewPos = transform.position;
viewPos.x = Mathf.Clamp(viewPos.x, screenBounds.x * -1 + objectWidth, screenBounds.x - objectWidth);
viewPos.y = Mathf.Clamp(viewPos.y, screenBounds.y * -1 + objectHeight, screenBounds.y - objectHeight);
Debug.Log(screenBounds);
Debug.Log(viewPos);
transform.position = viewPos;
}
I'd say it's not very usual having the player implemented as a UI element, and instead you should be implementing it outside the UI/Canvas system.
The UI/Canvas system uses a set of rules of placing and scaling to deal with responsive design. You have at least 4 values (excluding rotation) to place something on the screen: anchor, pivot, position and scale.
For example: if you want to create a square you can either set it's size in absolute pixel values or relative values (to parent). If you're using absolute values, your UI Scale Mode, defined on the Canvas object, should affect the visual results.
This means the UI/Canvas is for elements that should adapt to the screen, such as buttons, dialogs, labels, etc. Taking advantage of device parameters to improve the UX.
Outside the UI/Canvas system, things are directly based on Linear Algebra: you have a 3D vector space (a "World") where everything exists with an absolute size and position. Then, your Camera stretches and twists the whole world to match what your current perspective. That means your object will always have the same size, regardless of screen size.
Now, assuming you have a very specific reason to implement your game into UI, there are a few ways you can do it. I'll assume you're using absolute values. Please note all the units used here are pixels, and the effect should be different for devices with different resolutions and sensible to the UI Scale Mode parameter. Also, please note I've set both anchors min and max to (0,0), the bottom left corner (default is screen center, (0.5,0.5)), in order to avoid negative coordinates.
The following script is attached to the player's UI Image.
public class UIMovementController : MonoBehaviour
{
public float speed = 5.0f;
new private RectTransform transform;
private Rect canvasRect;
private void Start()
{
transform = GetComponent<RectTransform>();
canvasRect = GetComponentInParent<Canvas>().pixelRect;
}
void Update()
{
// Keyboard Input (Arrows)
Vector2 move = new Vector2(0,0);
if (Input.GetKey(KeyCode.UpArrow)) { move.y += speed; }
if (Input.GetKey(KeyCode.DownArrow)) { move.y -= speed; }
if (Input.GetKey(KeyCode.LeftArrow)) { move.x -= speed; }
if (Input.GetKey(KeyCode.RightArrow)) { move.x += speed; }
transform.anchoredPosition += move;
// Position clamping
Vector2 clamped = transform.anchoredPosition;
clamped.x = Mathf.Clamp(clamped.x, transform.rect.width / 2, canvasRect.width - transform.rect.width / 2);
clamped.y = Mathf.Clamp(clamped.y, transform.rect.height / 2, canvasRect.height - transform.rect.height / 2);
transform.anchoredPosition = clamped;
}
}
My character is a car and I try to rotate it the direction it move, so far so good I succeeded to do that but once I stop moving the character flips back to the direction it was on the start.
Also how can I make my turns from side to the opposite site smooth ?
Here is my code so far:
[SerializeField] float driveSpeed = 5f;
//state
Rigidbody2D myRigidbody;
// Start is called before the first frame update
void Start()
{
myRigidbody = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update()
{
Move();
}
private void Move()
{
//Control of velocity of the car
float HorizontalcontrolThrow = CrossPlatformInputManager.GetAxis("Horizontal"); // Value between -1 to 1
float VerticalcontrolThrow = CrossPlatformInputManager.GetAxis("Vertical"); // Value between -1 to 1
Vector2 playerVelocity = new Vector2(HorizontalcontrolThrow * driveSpeed, VerticalcontrolThrow * driveSpeed);
myRigidbody.velocity =playerVelocity;
**//Direction of the car**
Vector2 direction = new Vector2(HorizontalcontrolThrow, VerticalcontrolThrow);
float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
myRigidbody.rotation = angle;
}
I'm not sure about this, but maybe that last line "myRigidbody.rotation = angle" being called every frame is what is making your car reset its rotation.
Maybe change it to "myRigidbody.rotation *= angle" or "myRigidbody.rotation += angle".
It looks like it may be because HorizontalcontrolThrow and VerticalcontrolThrow are going to be reset when you release the controls. If it's resetting to its original orientation, then what's happening is that until you move, those two values are going to be at their default value. You then move and it affects the rotation. But when you release the controls, those values are back to the starting values again, and so is your rotation.
What you therefore need to do is try to separate the HorizontalcontrolThrow and VerticalcontrolThrow from the rest of the code, which should only be activated when at least one of these two variables are not at their default setting (I can't remember what the axis functions return at the moment).
Edit:
An IF statement should suffice (some rough pseudo code):
if (horizontalAxis != default || verticalAxis != default)
{
Rotate/Move
}
I solved the snap rotation using Quaternion at rotation, the issiu I had with it was to convert it from 3d to 2d, through the guide of this clip: youtube.com/watch?v=mKLp-2iseDc and made my adjustments it works just fine !
In a Unity VR app I'm building, I'm trying to rotate an object that is currently 'being held'. The technique I'm using is to find the desired rotation and feed it into the target objects Rigidbody via Rigidbody.MoveRotation. This works well when I use a test quaternion exposed in the inspector, but my quaternion math is completely failing me when it comes to finding the target rotation I want. I've got the position tracking working perfectly, it's just the rotation thats killing me!
The desired outcome is that the target object in question keeps its current rotation (from the moment its picked up) and simply applies the frame-by-frame difference of the VR controller. I thought this would be pretty straight forward, but I'm getting strange results. This is what I have so far:
public Rigidbody targetRigidbody;
public Quaternion controllerRotationSnapshot;
public Quaternion targetRotationSnapshot;
public float grabRadius = 0.25f;
public Collider[] grabResults;
Quaternion deltaRotation;
void Awake() {
grabResults = new Collider[5];
}
// This is triggered externally
public void Pickup() {
Physics.OverlapSphereNonAlloc(transform.position, grabRadius, grabResults);
for (int i = 0; i < grabResults.Length; i++) {
if (grabResults[i] != null) {
if (grabResults[i].CompareTag("Grabbable")) {
targetRigidbody = grabResults[0].GetComponent<Rigidbody>();
targetRotationSnapshot = targetRigidbody.transform.rotation;
i = grabResults.Length;
}
}
}
controllerRotationSnapshot = transform.rotation;
}
public void LetGo() {
if (!targetRigidbody) {
return;
}
targetRigidbody = null;
grabResults = new Collider[5];
}
void FixedUpdate() {
if (!targetRigidbody) {
return;
}
Quaternion deltaRotationDifference = transform.rotation * Quaternion.Inverse(controllerRotationSnapshot);
targetRigidbody.MoveRotation(targetRotationSnapshot * deltaRotationDifference);
}
It works perfectly as long as the target object is at a rotation of 0,0,0 (in euler angles). But the moment the target object is picked up with a different rotation at all (i.e. [X: 20, Y: -5, Z: 325] or something), the frame-by-frame controller difference (a 'delta rotation' I've heard this called?) gets applied on a completely different axis. It's very confusing because as far as I can tell, nothing about my code is applied locally, it's all using global calculations?
I've done a lot of Googlin' and can't find an answer at all, any help is greatly appreciated.
Generally for Unity I design all my artwork in Illustrator. So I start off with 1440x1920 (for portrait games) and outline a red frame of 1080x1920. So everything that fits well within the 1080x1920 usually covers the family of iOS devices for me. Then I set the Pixels Per Unit to 192 for all my images. This approach has really served me well. Now that the iPhone X is in the mix, how can I cater for it in a similar way?
As My experience, you don't have to do many things, the gameobject in a scene will just fit well, Unity has done that for you the only thing you maybe need to do is adjust the UI canvas if you are using UGUI, and it pretty easy too, why? because most phones have the similar width/height ratio, and in our team, we do the fix thing like this:
design all the UI stuff based on 1080p(1080x1920) (720p is fine too, but we presume there will be more 1080p device in the future)
attach following scripts to all the canvas to make an auto-fix thing:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class CanvasScreenAutoFix : MonoBehaviour
{
public static CanvasScreenAutoFix instance;
private static float DEPEND_W = 1080;
private static float DEPEND_H = 1920;
public float scaleRatio = 1.0f;
private void ResizeCanvas()
{
int screenW = Screen.width;
int screenH = Screen.height;
if (DEPEND_W == screenW && DEPEND_H == screenH)
{
scaleRatio = 1.0f;
return;
}
else
{
float W_scale = screenW / (float)DEPEND_W;
float H_scale = screenH / (float)DEPEND_H;
float scale = W_scale < H_scale ? W_scale : H_scale;
GetComponent<CanvasScaler>().scaleFactor = scale;
scaleRatio = scale;
}
}
private void Awake()
{
ResizeCanvas();
if(instance == null)
{
instance = this;
}
}
}
And the result turns to be pretty well, actually, we don't have to care about the scale things with the iPhone Family anymore after this(there may be some issue with iphone4 cause they have a different aspect ratio, but as there a rarely that devices anymore ...).