Unity 2017: 2D Screen Origin - c#

Super basic question that I cant seem to find an answer to:
All I've done is create a new 2D unity project and added a single sprite to the scene. I add a C# script to the sprite and set its position to a new Vector2(0,0). When run, the sprite moves its center point to the center of the screen, as opposed to the bottom left corner. This is also the case when I set the position of the spite to 0,0 in the editor. I've tried making the sprite a child of the camera, and I've tried using WorldToScreenPoint(). It's been awhile since I've used Unity so I must not be understanding something.
Thanks for the help
Edit:
As suggested here's the single line of code in question, don't think this will add much clarity.
public class NewBehaviourScript : MonoBehaviour {
// Use this for initialization
void Start () {
this.transform.position = new Vector2 (0, 0);
}
// Update is called once per frame
void Update () {
}
}

Unity renders based on the camera's frustum, not an arbitrary screen pixel position (unless you translate such to the frustum through a script).
By default, the camera will "look" at the scene-space origin (0, 0), and if you put your sprite there, of course it will be centered on screen on play.
[EDIT]
BTW, from...
I've tried making the sprite a child of the camera, and I've tried
using WorldToScreenPoint().
... it seems you're trying to put a sprite somewhere in relation to the camera. If that's for GUI, the correct way to do it is not as a child of the camera. Instead, use a screen-space Canvas. Screen-space canvasses, unlike cameras, work based on pixel position, too! =)

Related

Object following mouse using ScreenPointToRay lagging

I am building a 3d topdown shooter. The player controls the avatar with the keyboard and the reticule with the mouse.
I found a simple way to implement the reticule based on this article:
https://gamedevbeginner.com/how-to-convert-the-mouse-position-to-world-space-in-unity-2d-3d/
I defined an object which represents the reticule and attached this script:
public class Reticule : MonoBehaviour
{
Camera mainCamera;
Plane plane;
float distance;
Ray ray;
// Start is called before the first frame update
void Start()
{
mainCamera = Camera.main;
plane = new Plane(Vector3.up, 0);
// This would be turned off in the game. I set to on here, to allow me to see the cursor
Cursor.visible = true;
}
// Update is called once per frame
void Update()
{
ray = mainCamera.ScreenPointToRay(Input.mousePosition);
if (plane.Raycast(ray, out distance))
{
transform.position = ray.GetPoint(distance);
}
}
}
This works, but the issue is that the reticule is lagging behind the mouse cursor, and catches up when I stop moving the mouse.
Is this because this method is just slow. Is there another simple way to achieve the same result?
the issue is that the reticule is lagging behind the mouse cursor, and catches up when I stop moving the mouse
That's normal, your mouse cursor is drawn by the graphics driver (with the help of WDM) as fast as the mouse data information comes over the wire, while your game only renders at a fixed framerate (or slower). Your hardware mouse will always be ahead of where your game draws it or anything related to it.
Some things that can help with working around this:
Don't show the system cursor, instead show your own. That way the cursor you show will always be in the same place your game thinks it is (since it drew it) and you won't have this issue. Careful with this however, because if your game's frame rate starts dipping it will be VERY noticeable when your cursor movement isn't smooth anymore.
Don't tie objects to your cursor. The issue doesn't show with normal interactions, like clicking buttons. You will notice this in RTS games when drawing boxes around units, but I struggle to think of another example of this.
Like above, but less restrictive, you could lerp the objects tied to your cursor in place, so they're always and intentionally behind it. It makes it look more gamey, which isn't that bad for a, you know, game. Though I wouldn't do this for a twitchy shooter, of course.

Instantiate prefabs to a canvas at mouse coordinates

In a project I'm currently working on, I am having users click on an area for where they want to place things into the environment later on down the line. I'd like to visualize what they're placing with simple markers placed on the canvas, so that as they add and remove points the markers come and go as well.
I've found some resources on how to start, listing how to instantiate prefabs into the canvas, but it never seems to work for me. I feel it must have something to do with how I'm using coordinates but I'm not entirely sure.
public GameObject markerPrefab;
Then later on in another function
GameObject boatMarker = Instantiate(markerPrefab, Input.mousePosition, Quaternion.identity);
boatMarker.transform.SetParent(GameObject.FindGameObjectWithTag("Canvas").transform, false);
The code runs, and the prefabs do spawn into the scene, but they all appear in the top right hand corner of the canvas, all sort of one on top of the other. Any ideas what I've done wrong here? Also, while I don't want to ask you guys to write my code for me, any suggestions for a jumping point on how to remove specific instances of the prefab later down the line?
The main issue I'ld say is that you are using SetParent with the second parameter false
If true, the parent-relative position, scale and rotation are modified such that the object keeps the same world space position, rotation and scale as before.
In your case you want to keep the same world space position.
Since your canvas is Screenspace overlay its width and height (in Unity units) match exactly the display/window pixel width and height. Therefore when you do
GameObject boatMarker = Instantiate(markerPrefab, Input.mousePosition, Quaternion.identity);
The object already is in the correct position. To visualize that I just gave it a cube as child so you see it spawns already where I clicked (you can't see the image yet beacuase it's not a child of a Canvas):
What happens if you pass in that false parameter to the SetParent is that it doesn't keep it's current worldspace position but instead keeps its current localPosition and moves to that relative position within its parent. Since it is a Canvas and your prefab probably also using a RectTransform the resulting position depends on a lot of things like the e.g. the pivot and anchor settings of the prefab's RectTransform but also e.g. the Canvas Scaler -> Scale Factor.
If your prefab e.g. is anchored on the center (usually the default) and you click exactly on the center of your window it will appear on the upper right corner instead.
Why?
you click at (windowWidth / 2, WindowHeight / 2). So the prefab is originally spawned here
Than you use SetParent with false so it keeps that position but this time relative to the center of the Canvas
=> Center of canvas position is (windowWidth / 2, WindowHeight / 2) so adding the local prefab coordinates (windowWidth / 2, WindowHeight / 2) results in the final position (WindowWidth, WindowHeight) == upper right corner.
So you could fix that either by making the prefab beiing anchored on the lower left corner
or do not pass false as parameter to SetParent.
boatMarker.transform.SetParent(_canvas.transform);
you could actually then also do it in one single call
Instantiate(markerPrefab, Input.mousePosition, Quaternion.identity, _canvas.transform);
Additionally you should not use FindObjectWidthTag again and again. I would rather only get it once or even reference it via the Inspector if possible:
public GameObject markerPrefab;
[SerializeField] private Canvas _canvas;
private void Awake()
{
// If no canvas is provided get it by tag
if (!_canvas) _canvas = GameObject.FindGameObjectWithTag("Canvas").GetComponent<Canvas>();
}
// Update is called once per frame
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
Instantiate(markerPrefab, Input.mousePosition, Quaternion.identity, _canvas.transform);
}
}
Try This :
1- insert the button into the Canvas
2- get a Gameobject reference for this button
3- try this code after assigning your button to the button variable and the Game Canvas to the code Canvas variable, and it will work fine
public GameObject refButton;
public GameObject canvas;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (Input.GetMouseButtonDown(0))
{
GameObject button = Instantiate(refButton, Input.mousePosition,Quaternion.identity);
button.transform.SetParent(canvas.transform);
}
}

Unity5-GUI-HP-Bar Issue

So I've designed a custom HP bar and aligned it where I'd like it to be as well as how I'd like it to look.
However, when I press play (Not full screen mode, haven't even tested for that)either the image background slides slightly right or the green filler image slides to the left.
I have no idea why it's doing this or how to fix it. I'm willing to offer whatever information you require such as code or screenshots of the inspector.
This is a screen shot of the bar as it is in the sceneview canvas.
As you can see when the Play isn't pressed the bar functions normally. The above pic is Half-Full. The below image is Empty.
Part 2 of the issue:
I'm also having trouble with the HP bar rotating properly with the player.
However, when I turn left or right or face up:
]9
So you can see the HP bar doesn't properly rotate with the player's movement; although it does follow perfectly, the bar doesn't rotate accordingly. I can provide some movement code and the code I use to track the position of the player. What I have done was created a sphere and attached it to the player. I then attached the script for tracking the player onto that sphere. I then removed the mesh render and box collider of the sphere.
Health Bar Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class PHealth : MonoBehaviour {
[Header("HP Bar Images")]
[SerializeField]
private Image HpBarBG;
[SerializeField]
private Image HpBarFillBar;
private float imgFill = 1;
void Update () {
FillBar()
}
private void FillBar(){
HpBarFillBar.fillAmount = imgFill;
}
}
Player Movement Script
public GameObject player;
public Vector3 localscale;
// public Transform start, end;
[SerializeField]
private float speed = 5;
void Start () {
}
// Update is called once per frame
void Update ()
{
/*
******** RAYCAST TO DETECT WALLS BELOW *******
*/
// WallDetection(); //Cast ray to detect walls
/*
********* MOVEMENT CODE BELOW *********
*/
if (Input.GetKey(KeyCode.W)|| Input.GetKey(KeyCode.UpArrow)) // Move
Forward
{
player.transform.Translate(Vector2.up * speed * Time.deltaTime,
Space.World);
transform.eulerAngles = new Vector2(0, 180);
}
}//End of class
I'd recommend adding your code to your question so we have the full picture. Before you do that, I can only give you some recommendations and suggestions. I'll amend this answer, so it becomes a real answer, afterwards.
In the mean time, what type of canvas are you using? Judging from the hierarchy, I'd imagine it's in either of the screen space modes. Have you considered a world-space canvas parented to your player? I believe it'll naturally rotate in the way that you want it to. Can the players zoom in and out, and is the player character's rotation fixed to 90-degree increments?
In addition, are you sure you want the healthbar to rotate? To be upside down? Won't it be a better idea to keep it fixed in a regular position, above the character's sprite, even if that's technically "below" the character at the time?
Finally, if you don't mind me asking: why Unity 5? It's been a couple of major releases after it, and 2018 is almost at its cycle's end. Though to be fair, I don't know if the version will make any difference for this, so I'm just being curious.

Place scene object to get behind UI element

I have an object in my scene's sky and want to prevent it from getting cut out of the view, no matter the screen ratio. So I set up a canvas and a PlaceHolder RectTransform on it, that - as being UI element - gets scaled and repositioned in respect to the screen aspect ratio. My goal is to use this position to determine where to put my scene object by using the following script:
Camera mainCam;
RectTransform placeHolder;
private void Start()
{
mainCam = GameObject.FindGameObjectWithTag("MainCamera")
.GetComponent<Camera>();
placeHolder = GameObject.FindGameObjectWithTag("SkyObjectPlaceholder")
.GetComponent<RectTransform>();
var screenPos = mainCam.ScreenToWorldPoint(
new Vector3(placeHolder.rect.x, placeHolder.rect.y, 0));
transform.position = new Vector3(screenPos.x, screenPos.y,
transform.position.z);
}
The issue is that my sky object is always placed in the center of the screen. It is not a child of anything on the scene, I just put it there. The placeholder is to the upper right in the canvas. Note that I tried playing with anchors for the UI element, set it to be left bottom, right top, no change, the scene object is always in the center of the screen.
What am I doing wrong? How to make a Scene element match up the position of a Canvas element, so it gets right behind it?
Note: I'm using a perspective camera. I don't know if it matters, but I thought I mention it, just in case.
Yet another edit: after messing around with the issue a bit, I found that the problem is possibly linked to the Z input I give the ScreenToWorldPoint, in my case it's 0. I couldn't get a viable Z to input though.
Hmmm... i think you should change the sorting layer of that object

making my player sprite land on top of my platform sprite

in my XNA game(im fairly new to XNA by the way) i would like to have my player sprite land on top of a platform. i have a player sprite class that inherits from my regular sprite class, and the regular sprite class for
basic non playable sprite stuff such as boxes, background stuff, and platforms. However, i am unsure how to implement a way to make my player sprite land on a platform.
My player Sprite can jump and move around, but i dont know where and how to check to see if it is on top of my platform sprite.
My player sprites jump method is here
private void Jump()
{
if (mCurrentState != State.Jumping)
{
mCurrentState = State.Jumping;
mStartingPosition = Position;
mDirection.Y = MOVE_UP;
mSpeed = new Vector2(jumpSpeed, jumpSpeed);
}
}
mStartingPosition is player sprites starting position of the jump, and Position is the player sprites current position. I would think that my code for checking to see whether my player sprite is on top of my platform sprite. I am unsure how to reference my platform sprite inside of the playersprite class and inside of the jump method.
i think it should be something like this
//platformSprite.CollisonBox would be the rectangle around the platform, but im not
//sure how to check to see if player.Position is touching any point
//on platformSprite.CollisionBox
if(player.Position == platformSprite.CollisionBox)
{
player.mDirection = 0;
}
Again im pretty new to programming and XNA, and some of this logic i dont quite understand so any help on any of it would be greatly appreciated:D
Thanks
If player.Position is a Point and CollisionBox is a Rectangle, you could use
if (platformSprite.CollisionBox.Contains (player.Position))
I was playing around with something like this just a few days ago.
What you're calling a CollisionBox I called a BoundingBox. A BoundingBox is a Rectangle which represents the area occupied by the sprite.
You'll probably find it helpful to define a BoundingBox for your sprites instead of just using their position.
You can easily test for the collision of Rectangles using the following code:
if (player.BoundingBox.Intersects(platform.BoundingBox)
{
// handle collision here...
}
For this to work, make sure that the X and Y coordinates of your BoundingBox are correctly reflecting your sprite's position.
You may want to look at the Platformer Starter Kit. It includes this as well as detecting when the player is touching an enemy, collecting gems, etc.
The answer about BoundingBox is right, but IMHO it is easier to use Rectangle if you are making a 2D game rather than BoundingBox, wich is designed for a 3D one.
Anyway both objects have intersection / collision test methods that takes Vector2 or Point as parameter.
Now you have to have a box for your player, and a box for your platform. If one collide with the other, you have to check from where (the player can hit the platform from up, down, left, right, or maybe up & left on the left up corner).
If the player is on top of the platform, then just stop his fall, probably by setting his Y-speed component to 0.
Maybe you will need the player to go through the platform when jumping (player hit from bottom) but not when falling (player hit from top).

Categories