im kinda stuck with this problem.The idea is to have the blue square (player2) move when the top half of the screen is touched and the red square (player1) when the bottom half is touched.
I tried to make rect for the top and and bottom half and telling for example the red one to only move when the touch is within the bottom rect.
But the current behavior is like this: https://www.youtube.com/watch?v=yyVzuXFXipM
Player1 Script: https://www.codepile.net/pile/jAN8RgMl
Player2 Script: https://www.codepile.net/pile/6dxO7lX3
The coords i determined i would need:
coords in the middle might be inaccurate but the corners should be correct.
In general please post your code here and not in some external link!
At first glance: Be aware that you would have to provide the texts in screen pixel coordinates!
You are once starting at y = 0 and once at y = 5 .. that's just 5 pixels from the bottom!
You probably would rather want to use
var bottomRect = new Rect(0, 0, Screen.width, Screen.height / 2f);
and
var topRect = new Rect(0, Screen.height / 2f, Screen.width, Screen.height / 2f);
Btw If this is the only difference between your scripts you shouldn't use two different scripts but rather use the same script and only use different Rects like e.g.
public enum Player
{
Top,
Bottom
}
// Adjust this via the Inspector
public Player player;
and then
private Rect rect;
private void Awake ()
{
rect = player == Player.Top ? new Rect(0, Screen.height / 2f, Screen.width, Screen.height / 2f) : new Rect(0, 0, Screen.width, Screen.height / 2f);
}
Related
What do I want to achieve ?
I'd like to achieve an effect in Unity3D, where I superpose a few cameras on top of each other. Each cameras would draw to a specific area of the screen. If possible, I'd like these areas to change dynamically.
I am using unity (latest version), and URP.
How technically I see it :
For implementation and performances reasons, it seems writing to the stencil buffer is the way to go. That way, I can only render what part of the screen I want for each camera. It is also quite easy once the stencil is made, cause the ForwardRendering settings in Unity offer such capabilities out of the box.
What I can't figure out :
The problem is, I don't know to efficiently write to the whole stencil buffer (each frame). The best way would be to use a compute shader (or maybe a simple script), that directly write the values after some calculations. Is there a way for that ? If yes, How ?
Another alternative may be to use a transparent quad in front of one of each camera, and to write to the stencil buffers like that. But 1) It seems there exist a SV_StencilRef keyword in the fragment buffer, but not supported by Unity yet ? 2) I will still lose performance nevertheless.
Thanks for any help / ideas about how to tackle this problem.
Edit (Clarification) : I'd like to be able to render free shapes, and not only rects, which prevent the use of the standard ViewportRect.
After some search, I found the Voronoi split screen to be quite similar (with a technical view) to what I'd like to achieve (See here)
If I understand correctly, you only need to play with the different camera Viewport Rect (https://docs.unity3d.com/ScriptReference/Camera-rect.html) to determine what camera should render what part of the screen.
Response to comment: no, it's not stretched. Here is an example with four cameras:
Create a scene with four cameras, add this script to it and add the cameras to the array on the script. I added the _movingObject just to see something moving, but it's not necessary.
using UnityEngine;
public class CameraHandler : MonoBehaviour
{
[SerializeField] private Transform _movingObject;
[SerializeField] private float _posMod = 10.0f;
[SerializeField] private float _cameraPosMod = 0.1f;
[SerializeField] private Camera[] _cameras;
private void Update()
{
float t = Time.time;
float x = Mathf.Sin(t);
float y = Mathf.Cos(t);
if (_movingObject) _movingObject.position = new(x * _posMod, 1.0f, y * _posMod);
Vector2 center = new(0.5f + x * _cameraPosMod, 0.5f + y * _cameraPosMod);
// bottom left camera
_cameras[0].rect = new(0.0f, 0.0f, center.x, center.y);
// bottom right camera
_cameras[1].rect = new(center.x, 0.0f, 1.0f - center.x, center.y);
// upper left camera
_cameras[2].rect = new(0.0f, center.y, center.x, 1.0f - center.y);
// upper right camera
_cameras[3].rect = new(center.x, center.y, 1.0f - center.x, 1.0f - center.y);
}
}
Not exactly an answer to your question about stencil buffer but I had a (hopefully) similar use case recently.
The main issue: In the URP Camera stack
If your camera is set to Base it will overdraw the entire screen
you can not adjust the Viewport on any Overlay camera
You can actually try to set the viewport via code -> result your camera renders only the correct part of the scene ... but it gets stretched to the entire screen ^^
What I did in the end was
Leave all content and cameras at the origin position
Apply according masks to filter the content per camera
Make your camera Overlay (as usual)
go through a custom Camera.projectionMatrix
m_Camera.projectionMatrix = Matrix4x4.Translate(projectionOffset) * Matrix4x4.Perspective(m_Camera.fieldOfView, m_Camera.aspect, m_Camera.nearClipPlane, m_Camera.farClipPlane);
where the projectionOffset is an offset in viewport space (normalized 0 - 1) from the bottom left corner.
For example in my case I wanted a minimap at 400, 400 pixels from the top-right corner so I did
var topRightOffsetPixels = new Vector2(400, 400);
var topRightOffsetViewport = Vector2.one - new Vector2(topRightOffsetPixels.x * 2 / Screen.width, topRightOffsetPixels.y * 2 / Screen.height);
m_Camera.projectionMatrix = Matrix4x4.Translate(topRightOffsetViewport) * Matrix4x4.Perspective(m_Camera.fieldOfView, m_Camera.aspect, m_Camera.nearClipPlane, m_Camera.farClipPlane);
See also Matrix4x4.Perspective
The code I'm using is
public void Strech(GameObject sprite, Vector3 initialPosition, Vector3 finalPosition)
{
Vector3 centerPos = (initialPosition + finalPosition) / 2f;
sprite.transform.position = centerPos;
Vector3 direction = finalPosition - initialPosition;
direction = Vector3.Normalize(direction);
sprite.transform.right = direction;
distance = Vector3.Distance(initialPosition, finalPosition);
Debug.DrawLine(initialPosition, finalPosition);
sprite.GetComponent<RectTransform>().sizeDelta = new Vector3(distance, 40f);
}
I can't seem to figure out why the image won't tile to or from either centers of the UI objects.
What am I doing wrong? I call the Stretch function in the Update loop.
The red line itself is a UnityEngine.UI Image type.
I want the image to tile as shown by the Debug.DrawLine
Edit 1: Here's how the rect is being displayed
I fixed it!
I had to multiply a scalefactor, because the Canvas was using a Canvas Scaler with reference resolution of 1280 x 720
I have an image UI in a canvas with Screen Space - Camera render mode. What I like to do is move my LineRenderer to the image vertical position by looping through all the LineRenderer positions and changing its y axis. My problem is I cant get the correct position of the image that the LineRenderer can understand. I've tried using ViewportToWorldPoint and ScreenToWorldPoint but its not the same position.
Vector3 val = Camera.main.ViewportToWorldPoint(new Vector3(image.transform.position.x, image.transform.position.y, Camera.main.nearClipPlane));
for (int i = 0; i < newListOfPoints.Count; i++)
{
line.SetPosition(i, new Vector3(newListOfPoints[i].x, val.y, newListOfPoints[i].z));
}
Screenshot result using Vector3 val = Camera.main.ScreenToWorldPoint(new Vector3(image.transform.localPosition.x, image.transform.localPosition.y, -10));
The green LineRenderer is the result of changing the y position. It should be at the bottom of the square image.
Wow, this was annoying and complicated.
Here's the code I ended up with. The code in your question is the bottom half of the Update() function. The only thing I changed is what was passed into the ScreenToWorldPoint() method. That value is calculated in the upper half of the Update() function.
The RectTransformToScreenSpace() function was adapted from this Unity Answer post1 about getting the screen space coordinates of a RectTransform (which is exactly what we want in order to convert from screen space coordinates back into world space!) The only difference is that I was getting inverse Y values, so I changed from Screen.height - transform.position.y to just transform.position.y which did the trick perfectly.
After that it was just a matter of grabbing that rectangle's lower left corner, making it a Vector3 instead of a Vector2, and passing it back into ScreenToWorldPoint(). The only trick there was because of the perspective camera, I needed to know how far away the line was from the camera originally in order to maintain that same distance (otherwise the line moves up and down the screen faster than the image). For an orthographic camera, this value can be anything.
void Update () {
//the new bits:
float dist = (Camera.main.transform.position - newListOfPoints[0]).magnitude;
Rect r = RectTransformToScreenSpace((RectTransform)image.transform);
Vector3 v3 = new Vector3(r.xMin, r.yMin, dist);
//more or less original code:
Vector3 val = Camera.main.ScreenToWorldPoint(v3);
for(int i = 0; i < newListOfPoints.Count; i++) {
line.SetPosition(i, new Vector3(newListOfPoints[i].x, val.y, newListOfPoints[i].z));
}
}
//helper function:
public static Rect RectTransformToScreenSpace(RectTransform transform) {
Vector2 size = Vector2.Scale(transform.rect.size, transform.lossyScale);
Rect rect = new Rect(transform.position.x, transform.position.y, size.x, size.y);
rect.x -= (transform.pivot.x * size.x);
rect.y -= ((1.0f - transform.pivot.y) * size.y);
return rect;
}
1And finding that post from a generalized search on "how do I get the screen coordinates of a UI object" was not easy. A bunch of other posts came up and had some code, but none of it did what I wanted (including converting screen space coordinates back into world space coordinates of the UI object which was stupid easy and not reversibe, thanks RectTransformUtility!)
I was trying to create a game the involves the user walking into the screen. I found some idea on how to do this here being lead by this question. But the tutorial is based on GLES1 and I could not adopt it for GLES2.
Here is what the design looks like,
Here is what I tried so far,
I modified GLState and added
public void frustumProjectionGLMatrixf(final float fov_degrees, float aspect, float zNear, float zFar) {
this.mProjectionGLMatrixStack.glFrustumf(fov_degrees, aspect, zNear, zFar);
}
and I modified GLMatrixStack and added
public void glFrustumf(float fov_degrees, float aspect, float zNear, float zFar) {
float[] mTemp = new float[2 * GLMatrixStack.GLMATRIX_SIZE];
Matrix.setIdentityM(this.mMatrixStack, this.mMatrixStackOffset);
// Matrix.frustumM(this.mMatrixStack, this.mMatrixStackOffset, 0, this.getWidthRaw(), this.getHeightRaw(), 0, 10, 300);
Matrix.perspectiveM(this.mMatrixStack, this.mMatrixStackOffset, fov_degrees, aspect, zNear, zFar);
Matrix.setLookAtM(this.mMatrixStack, this.mMatrixStackOffset, 0, 120f, zNear * 10, 0, 0, 0f, 0, 1, 0);
Matrix.scaleM(this.mMatrixStack, this.mMatrixStackOffset, 1, -1, 1);
System.arraycopy(this.mMatrixStack, this.mMatrixStackOffset, mTemp, GLMatrixStack.GLMATRIX_SIZE, GLMatrixStack.GLMATRIX_SIZE);
Matrix.multiplyMM(this.mMatrixStack, this.mMatrixStackOffset, mTemp, GLMatrixStack.GLMATRIX_SIZE, mTemp, 0);
}
and I created the camera as follows
this.camera = new Camera(0, 0, WIDTH, HEIGHT) {
#Override
public void onApplySceneMatrix(final GLState pGLState) {
super.onApplySceneMatrix(pGLState);
this.setZClippingPlanes(-3000, 3000);
setFrustum(pGLState);
}
#TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
private void setFrustum(GLState pGLState) { // set field of view to 60 degrees
float fov_degrees = 60;
float fov_radians = fov_degrees / 180 * (float) Math.PI;
float aspect = this.getWidth() / (this.getHeight());
float camZ = this.getHeight() / 2 / (float) Math.tan(fov_radians / 2);
pGLState.frustumProjectionGLMatrixf(fov_degrees, aspect, -100, 100);
pGLState.flush();
}
};
But there is nothing drawn on the screen. The scene is totally black. Any if there is no way to adapt perspective in AndEngine GLES2, other tricks to make this work will be appreciated.
Honestly i gave up and made my own pseudo 3D projection engine. The problem with this is that it is very slow, as it is all done without GL coding.
I had to use Sprite groups and 1 really big mesh(Mesh being the road), which really hurt the layering on the Z axis if you have rolling hills or tunnels etc. You will see in the pictures below.
In the end i had to create 3 sprite groups and 3 meshes. Detect if a sprite was behind the ground/tunnel/etc.
From that point backward(into the distance) i would
1: take sprites out of the first group and into the 2nd
2: stop all the first meshes vertices and draw the 2nd mesh from that point onward
3: repeat, if collision occurred behind the 2nd mesh, start the 3rd
hard to explain, took me a while to get it working and it's very quirky with a lot of restrictions. It took me months to get the FPS from 3 back to 60.
Without Layering (Trees show over the ground)
With collision detection layering
I can go into more detail if you would like.
Also if anyone has a better way of doing this, drop in some information for us.
am very much new in Unity3D. Tried to watch some youtube video tutorials. But am having a doubt. I have an object which is placed at the top-right position using the following code at game startup:
myObject.position = mainCam.ScreenToWorldPoint(new Vector3(Screen.width - 75, Screen.height ,0f));
Based on the docs, the (0,0) position in camera viewport is on the left bottom corner and the (1,1) position is on top right corner. That's why I used the following values in the above line:
x = Screen.width - 75; // to position 75px from right side
y = Screen.height; // at top on y-axis
z = 0; // not needed
What I to do is, myObject should move up and down continuously. ie, it should move from top to bottom and vice versa, as a loop. Something like this(the ball moving from top to bottom and viceversa):
While looking for solution, I found an answer. And was trying to tweak it. The object is moving, but it is not moving correctly. It's going sideways! The following tweaked script is used in myObject:
#pragma strict
var mainCam : Camera;
function Start () {
var pointA : Vector3 = transform.position;
var pointB : Vector3 = mainCam.ScreenToWorldPoint(new Vector3(transform.position.x, transform.localScale.y/2 ,0f));
while (true) {
yield MoveObject(transform, pointA, pointB, 3.0);
yield MoveObject(transform, pointB, pointA, 3.0);
}
}
function MoveObject (thisTransform : Transform, startPos : Vector3, endPos : Vector3, time : float) {
var i = 0.0;
var rate = 1.0/time;
while (i < 1.0) {
i += Time.deltaTime * rate;
thisTransform.position = Vector3.Lerp(startPos, endPos, i);
yield;
}
}
But the movement is towards bottom left corner! I have been trying to figure it out for hours now! Any guess on where it went wrong? Or if you have better solutions, I would really appreciate.
Gonna take a stab at posting this as an answer (heaps easier than doing multiple lines in comments anyway).
So, we grab the top-right and bottom-right corners of the screen in pixel dimensions (using 75 as a margin):
var screenPointA:Vector3 = Vector3(Screen.width-75, Screen.height-75, 0);
var screenPointB:Vector3 = Vector3(Screen.width-75, 75, 0);
Then we get the world positions that the object will loop back and forth between:
var pointA:Vector3 = mainCam.ScreenToWorldPoint(screenPointA);
var pointB:Vector3 = mainCam.ScreenToWorldPoint(screenPointB);
If pointA.z or pointB.z are incorrect, you can change them after if required.
(happy to continue commenting/editing if needed to help you solve this!)