I need little help with function WorldToScreenPoint(position), Could somebody guide me little bit ? I am using this function to display name of the city:
public class LabelsTest : MonoBehaviour
{
[SerializeField]
private Text nameLabel;
// Update is called once per frame
void Update()
{
Vector3 cameraPos = Camera.main.WorldToScreenPoint(transform.position);
nameLabel.transform.position = cameraPos;
}
}
But problem is that I see UI with text two times, one above the plane which is perfect:
but when I face away from the plane, I can see the label there, too.:
I don't know if I am doing something wrong or it just not working as it should.
Thanks for help.
You need to prevent the Text from rendering while it is behind the camera. Luckily, WorldToScreenPoint gives you a z component that tells you how far in front of the camera the point is. So, just set the Text to be enabled when z>0 and disabled when z<=0:
public class LabelsTest : MonoBehaviour
{
[SerializeField]
private Text nameLabel;
// Update is called once per frame
void Update()
{
Vector3 cameraPos = Camera.main.WorldToScreenPoint(transform.position);
nameLabel.transform.position = cameraPos;
nameLabel.enabled = cameraPos.z>0;
}
}
That's because your 'Plane' is culling the backface, meaning when the Plane is up at a certain point higher than the Camera's Y position it goes invisible.
Here's a GIF of the problem.
https://gyazo.com/d22c51951d1e8abd07a354af7f48ffef
Related
this question is a bit long but please bare with me.
I am working with Unity3D's newer input system, and I've been able to set up camera movement so when the user clicks the middle mouse button, they can drag the camera around along the X- & Y-axis. However, has a very parallax-y feel to it, which I am trying to avoid. From my understanding this is happening because the speed of the mouse movement & the camera movements are not the same (See, visual).
My InputController class:
public class InputController : MonoBehaviour
{
private InputControls inputControls;
[Header("Camera Movement")]
[SerializeField] CameraController cameraController;
private InputAction.CallbackContext context;
void Awake()
{
inputControls = new InputControls();
}
private void Start()
{
inputControls.Mouse.MiddleButton.started += MiddleButton_started;
}
public void MiddleButton_started(InputAction.CallbackContext ctx)
{
context = ctx;
}
private void Update()
{
bool mouseIsDown = context.performed;
if (mouseIsDown)
{
Vector2 delta = inputControls.Mouse.DeltaPosition.ReadValue<Vector2>();
cameraController.Move(delta, false);
}
}
}
My CameraController class:
public class CameraController : MonoBehaviour
{
[SerializeField] Vector2 minPosition;
[SerializeField] Vector2 maxPosition;
[SerializeField] float speed = 3;
[SerializeField] float zPosition = -10f;
private Camera mainCam;
private void Start()
{
mainCam = GetComponent<Camera>();
}
public void Move(Vector2 deltaPosition, bool convertToViewportPoint = true)
{
if (convertToViewportPoint) deltaPosition = mainCam.ScreenToViewportPoint(deltaPosition);
Vector3 moveTo = new Vector3(deltaPosition.x, deltaPosition.y, 0);
Vector3 target = transform.position - moveTo;
float clampedX = Mathf.Clamp(target.x, minPosition.x, maxPosition.x);
float clampedY = Mathf.Clamp(target.y, minPosition.y, maxPosition.y);
transform.position = Vector3.Lerp(transform.position, new Vector3(clampedX, clampedY, -10), speed * Time.deltaTime);
}
}
Now, in this version of the CameraController, the parallax-y feel might be coming from the fact that the Lerp speed is constant, whereas the speed of the mouse movement is not.
However, I've tested it with various magnitudes (ie., the magnitudes of the delta mouse position, the current mouse position, the current mouse position subtracted from the delta mouse position, etc...) with generally the same results -- either the camera moves too fast for the mouse or vice versa.
From my limited but growing understanding of trig, magnitudes represents the speed of a vector but I CANNOT get the speed of the camera movement and the speed of the mouse movement to match up. I would like for this to happen so that when the user clicks a certain point, that point generally stays under the cursor when in movement (See other visual).
Can someone help me understand how I might achieve this?
Thanks kindly.
What I did to solve this, in case anyone comes across this, is get the position of the cursor when the mouse is first clicked. We can call that dragOrigin. Then we can get the change in position as the mouse moves (polling the input through Unity's input system can be done through an Update function). Then we subtract the new mouse position from dragOrigin. Once we have that difference -- lets call it deltaPosition -- we just add it to the camera's current position.
To ensure that the speed matches that of the mouse, run the deltaPosition and the currentMousePosition through SmoothDamp to get the velocity of the change. Its magnitude will be the speed you want the camera to move.
So for example:
// Call this when the user first clicks the mouse.
// Lets assume this is part of a class that controls the camera.
public void SetDragOrigin(Vector2 mousePosition)
{
// could probably cache Camera.main for efficiency
dragOrigin = Camera.main.ScreenToWorldPoint(mousePosition);
}
// This is called in an update function of another class that deals with input.
// (Or perhaps you can simply pass the input controls to the class controlling the camera, but that's not the case here).
public void Move(Vector2 newMousePosition)
{
mousePosition = mainCam.ScreenToWorldPoint(mousePosition);
Vector2 deltaPosition = dragOrigin - mousePosition;
Vector3.SmoothDamp(deltaPosition, mousePosition, ref dragVelocity, 0.1f, 250f, Time.deltaTime);
cameraPosition += new Vector3(deltaPosition.x, deltaPosition.y, 0) * dragVelocity.magnitude * Time.deltaTime;
// anything else you want to do like clamping camera position
// ....
}
One thing to note here, is that the 5th argument of SmoothDamp is the maxSpeed. I hardcoded it in this example, but you might want to play around with it. When I did not set the maxSpeed, there was some wonky things happening to the dragVelocity over time, so it may be better to set the maxSpeed to what suits you best.
I don't know how to get a certainly value from the opacity of my gameobject. I wanna do a laser, so I did an animation of my laser going from 0 to 230, and then back to 0. I want it to make damage only if it's in 230 of opacity but I don't know how ;(. Can somebody help me? this is a part of my animation
Color internally is stored as float values 0 to 1.
So assuming you have access to your object it would probably be something like e.g.
public class DamageController : MonoBehaviour
{
// Here you drag in the according object
[SerializeField] private Renderer renderer;
// Due to imprecision I would go one lower just to be sure
private float targetAlpha = 229f/255f;
private void Update ()
{
if(renderer.material.color.a >= targetAlpha)
{
// However you deal damage
DealDamage (amountPerSecond * Time.deltaTime);
}
}
}
However, checking the alpha isn't actually necessary!
You have an animation anyway so you can also simply have the component reduced like
public class DamageController : MonoBehaviour
{
private void Update ()
{
// However you deal damage
DealDamage (amountPerSecond * Time.deltaTime);
}
}
And now simply additionally animate the enabled state of this component!
Make it disabled by default and only enable it within the animation during the desired period.
I'm trying to come up with a generic rule that I can add to any GameObject. Items like tables, chairs etc that once collided and interacted fade in the white text, then after a few seconds the text fades out.
Ideally I'd be able to offset the position of this text with a Vector3 so I can position it around my character
At the moment I have, some code but the CrossFade.Alpha doesn't seem to work. this might or might not be the best way to go about this, I'm open to new ideas
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Qwerty : MonoBehaviour
{
public float Radius = 1f;
public Text TextToUse;
[SerializeField] private LayerMask playerMask;
private bool isInSphere;
// Start is called before the first frame update
void Start()
{
isInSphere = false;
}
// Update is called once per frame
void Update()
{
isInSphere = Physics.CheckSphere(transform.position, Radius, playerMask);
if (isInSphere && Input.GetKey(KeyCode.F) == true)
{
// Check if collision is detected and F is pressed
isInSphere = true;
Debug.Log("Hello");
//Fade in over 2 seconds
TextToUse.CrossFadeAlpha(1, 2f, false);
}
else if (!isInSphere && !Input.GetKey(KeyCode.F))
{
//Fade out on leave over 2 seconds
isInSphere = false;
TextToUse.CrossFadeAlpha(0, 2f, false);
}
}
}
Description
Tweens the alpha of the CanvasRenderer color associated with this Graphic.
Creates a fading effect on a Graphic with a CanvasRenderer attached. Choose an alpha level to fade to, and pick the speed of the fade to see a smooth fade over time. UI Images and Text are some of the elements that you are able to apply this effect to.
CrossFade.Alpha needs a canvas that has CanvasRenderer attached to work.
(in Unity 3D)
Hi.
I want to show "HPBar" of the character on UI Canvas.
I tried the following method, but it didn't work normally.
Please tell me how to solve this problem...
/// <summary>
/// Follow target's position
/// </summary>
/// <param name="target">target(character's world position)</param>
void ChaseCharacter(Vector3 target)
{
// change world position to screen position (Main Camera)
Vector3 p1 = _worldCam.WorldToScreenPoint(target);
// change screen position to world position (UI Camera)
Vector3 p2 = _uiCam.ScreenToWorldPoint(p1);
// Apply UI Position
transform.position = p2;
}
I've built a minimal example, with a single camera. I guess with some small tweaks it should work on your multi-camera case.
public class UIFollow : MonoBehaviour
{
public GameObject target;
public Vector2 offset;
new private Camera camera;
void Start()
{
camera = GetComponentInParent<Canvas>().worldCamera;
}
void Update()
{
((RectTransform)transform).anchoredPosition = camera.WorldToScreenPoint(target.transform.position) + (Vector3)offset;
}
}
UIFollow is a Component on the "HealthBar" object inside the Canvas. Make sure it's anchors are set to the left bottom corner.
The offset vector is used to place the health bar above the object's center, it's public so it's easier to modify while designing the game.
From the hierarchy tab, add a new object, section UI then select Image, a canvas object should appear if not already created, inside should be the white image created , this Image should already follow your camera to every place because its in the UI screen section not near the player, just resize, make transparent and add your health bar inside it.
Thank you all for your advice.
I solved this problem, but I don't know how it was solved.
I'll upload the resolved code.
Vector3 p1 = _worldCam.WorldToScreenPoint(target);
p1.z = 100;
Vector3 p2 = _uiCam.ScreenToWorldPoint(p1);
transform.position = p2;
I'm following this tutorial right now: https://unity3d.com/learn/tutorials/projects/roll-ball-tutorial/moving-camera?playlist=17141
I've managed to implement a 3rd person camera view button, but I'm having trouble figuring out how to do the same for a 1st person camera view. Below is a camera control script that I attached to the main camera:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class cameraControls : MonoBehaviour
{
public GameObject player;
private Vector3 offset;
public bool thirdPerson;
public bool firstPerson;
void OnGUI()
{
// 3rd person camera view
if (GUI.Button(new Rect(20, 50, 140, 40), "3rd Person Camera"))
{
thirdPerson = true;
}
// 1st person camera view
if (GUI.Button(new Rect(20, 110, 140, 40), "1st Person Camera"))
{
firstPerson = false;
}
}
// Start is called before the first frame update
void Start()
{
offset = transform.position - player.transform.position;
}
// Update is called once per frame
void Update()
{
if (thirdPerson == true)
{
transform.position = player.transform.position + offset;
}
}
}
Well, while it might be okay for such a small tutorial:
you should not use the GUI and OnGUI stuff for ingame UI. unity released in version 4.6 (years ago) a better UI system.
you could have 2 cameras, one for 3rd person one for 1st person. when pressing one of your buttons, you disable one camera and enable the other.
based on your edit:
you currently have 2 bool variables there, one for "firstPerson" one for "thirdPerson", thats redundant.
what do you do if both are true? or both are false? just have one variable e.g. "thirdPerson" true -> use 3rd person, false -> use 1st person.
also i see that you have decided to change the cameras position instead of using 2 cameras. this is also is a possible way to accomplish your goal
You can use a single camera and change its position based on which viewing angle should be active. Try to attatch two empty objects as children to your player object and add a reference to them in your script (your camera also needs to be a child of your player for this to work). Then drag & drop them from the hierarchy to the inspector and just switch between those positions like so:
public Transform firstPersonPosition;
public Transform thirdPersonPosition;
public Camera camera;
private void GoFirstPerson()
{
camera.transform.position = firstPersonPosition.position;
}
private void GoThirdPerson()
{
camera.transform.position = thirdPersonPosition.position;
}
You can basically use them as "waypoints" for your camera to jump to.
//Edit:
If you are having problems with understanding how your code affects your GameObjects during Play Mode just switch to Scene View during play and look at your objects and where they are in the scene. I bet your first person camera is somewhere in your player model because you set its position to your players position.