I have 4 sprites with colliders, I want to auto position and scale them to the bottom of the screen evenly so they don't over lap and they are not off screen at all. I can not do this in canvas it needs to be done as gameObjects.
I also am trying to get each Sprits height to be 1/4th-1/5th depending on how it looks, that's why the code is divided by 4 down below.
How do I get them to position on the bottome and side by side?
public class AutoPosition : MonoBehaviour {
public Sprite [] goals;
public float width = Screen.width / 4;
public float height = Screen.height / 4;
// Use this for initialization
void Start () {
for (int i = 0; i < goals.Length; i++) {
goals[i]
}
}
You can use SpriteRender for the images. And position them inside of a parent GameObject. Than it is enough to simply scale and position that one Parent GameObject correctly (similar to the canvas but with normal Transform components).
public class applySize : MonoBehaviour
{
private void Apply()
{
// Get the main camera position
var cameraPosition = Camera.main.transform.position;
// This makes the parent GameObject "fit the screen size"
float height;
if (Camera.main.orthographic)
{
// Camera projection is orthographic
height = 2 * Camera.main.orthographicSize;
}
else
{
// Camera projection is perspective
height = 2 * Mathf.Tan(0.5f * Camera.main.fieldOfView * Mathf.Deg2Rad) * Mathf.Abs(cameraPosition.z - transform.position.z);
}
var width = height * Camera.main.aspect;
transform.localScale = new Vector3(width, height,1);
transform.position = cameraPosition - new Vector3(0,height*.375f, cameraPosition.z);
// Since the 4 images are childs of the parent GameObject there is no need
// place or scale them separate. It is all done with placing this parent object
}
private void Start()
{
Apply();
}
}
Using the following Scene setup
The X positions of the Sprites simply are
image1: - width * 1.5;
image2: - width * 0.5;
image3: width * 0.5;
image4: width * 1.5;
and for the four SpriteRenderers
and the colliders
Result
(with an additional call in Update)
I made the Parent position stay on Z = 0. You can change this according to your needs.
This way the colliders should now be able to interact with other objects.
Related
I struggle to reduce the Y-Scale of a square, while shifting the Y-position downwards,
so the square stays in place.
It should be like a healthbar for remaining oxygen, but in a gameobject not on a canvas.
I reduce the scale of the square over time with this code:
float remainingAir = 100f;
void Start()
{
defaultY = oxygenOverlay.transform.localScale.y;
yPercent = defaultY / air;
}
float reduceFactor = newY * air;
void AdjustOxygenBar()
{
oxygenOverlay.transform.localScale = new Vector3(oxygenOverlay.transform.localScale.x, yPercent * remainingAir ,oxygenOverlay.transform.localScale.z);
}
The scale reduced from both sides of the square. How do I move the square downward each frame, so the square will only reduce from top to bottom?
Thanks on advance
Well I have tried this and it works to scale only on Y axis, however it will shrink towards the center of the object so yes you have to move it down to make the effect thats a bar thats being unfilled
void Awake ()
{
percent = 100;
}
void Update ()
{
percent -= 0.1f;
transform.localScale = new Vector3(transform.localScale.x,transform.localScale.y*percent/100,transform.localScale.z);
}
I'm trying to make a 2d fighting camera. I want the camera to focus on 2 fighters and then zoom in/out to make sure they are both on the screen. Using the post below, I was able to get this camera to work horizontally:
https://answers.unity.com/questions/126184/fighting-game-camera.html
The original script is the following
float margin = 1.5f; // space between screen border and nearest fighter
private float z0; // coord z of the fighters plane
private float zCam; // camera distance to the fighters plane
private float wScene; // scene width
private Transform f1; // fighter1 transform
private Transform f2; // fighter2 transform
private float xL; // left screen X coordinate
private float xR; // right screen X coordinate
public void calcScreen(Transform p1, Transform p2)
{
// Calculates the xL and xR screen coordinates
if (p1.position.x < p2.position.x)
{
xL = p1.position.x - margin;
xR = p2.position.x + margin;
}
else
{
xL = p2.position.x - margin;
xR = p1.position.x + margin;
}
}
public void Start()
{
// find references to the fighters
f1 = GameObject.Find("Fighter1").transform;
f2 = GameObject.Find("Fighter2").transform;
// initializes scene size and camera distance
calcScreen(f1, f2);
wScene = xR - xL;
zCam = transform.position.z - z0;
}
public void Update()
{
Vector3 pos = transform.position;
calcScreen(f1, f2);
float width = xR - xL;
if (width > wScene)
{ // if fighters too far adjust camera distance
pos.z = zCam * width / wScene + z0;
}
// centers the camera
pos.x = (xR + xL) / 2;
transform.position = pos;
}
Since on my game you can move horizontally and vertically, I modified the code to also adjust when the character moves vertically to this:
float margin = 1.5f; // space between screen border and nearest fighter
float marginY = 1.5f; // space between screen border and nearest fighter
private float z0; // coord z of the fighters plane
private float zCam; // camera distance to the fighters plane
private float wScene; // scene width
private float hScene; // scene height
private Transform f1; // fighter1 transform
private Transform f2; // fighter2 transform
private float xL; // left screen X coordinate
private float xR; // right screen X coordinate
private float yU; // up screen Y coordinate
private float yD; // down screen Y coordinate
public void calcScreen(Transform p1, Transform p2)
{
// Calculates the xL and xR screen coordinates
if (p1.position.x < p2.position.x)
{
xL = p1.position.x - margin;
xR = p2.position.x + margin;
}
else
{
xL = p2.position.x - margin;
xR = p1.position.x + margin;
}
if (p1.position.y < p2.position.y)
{
yD = p1.position.y - marginY;
yU = p2.position.y + marginY;
}
else
{
yD = p2.position.y - marginY;
yU = p1.position.y + marginY;
}
}
public void Start()
{
// find references to the fighters
f1 = GameObject.Find("Fighter1").transform;
f2 = GameObject.Find("Fighter2").transform;
// initializes scene size and camera distance
calcScreen(f1, f2);
wScene = xR - xL;
hScene = yU - yD;
zCam = transform.position.z - z0;
}
public void Update()
{
Vector3 pos = transform.position;
calcScreen(f1, f2);
float width = xR - xL;
float height = yU - yD;
if (width > wScene)
{ // if fighters too far adjust camera distance
pos.z = zCam * width / wScene + z0;
}
else if (height > hScene)
{ // if fighters too far adjust camera distance
pos.z = zCam * height / hScene + z0;
}
// centers the camera
pos.x = (xR + xL) / 2;
pos.y = (yU + yD) / 2;
transform.position = pos;
}
This works when the character only moves horizontally or vertically, if it moves both horizontally and vertically it doesn't work anymore. Also, the vertical movement doesn't work appropriately when the 2 fighters are far away. The camera zooms in even thought they are far from each other but they are close in terms of the height.
I feel like I need to change the code to look at both x and y at the same time. It feels like I'm considering and x separately.
I have been stuck on this for a while now. Any help would be appreciated.
Your else statement is the reason your vertical scaling is being removed. You should change your if block in Update to this.
if (width > wScene)
{ // if fighters too far adjust camera distance
pos.z = zCam * width / wScene + z0;
}
if (height > hScene)
{ // if fighters too far adjust camera distance
pos.z = Mathf.Max(zCam * height / hScene + z0, pos.z);
}
I'm creating a basic simulator in Unity for my A-level Computer Science project. At the moment the user is able to draw a box (crate) object by selecting the associated tool and clicking and dragging to determine two opposite corners of the box, thus determining its dimensions.
The box consists of a single prefab which is instantiated and has its size changed accordingly. The code for it is as follows:
void Start () {
boxAnim = boxButton.GetComponent<Animator>();
}
// Update is called once per frame
void Update()
{
//sets the mouseDown and mouseHeld bools and the mouse position Vector3
mouseDown = Input.GetMouseButtonDown(0);
mouseHeld = Input.GetMouseButton(0);
mousePosition = Input.mousePosition;
//checks if the user has started to draw
if (mouseDown && !draw)
{
draw = true;
originalMousePosition = mousePosition;
}
//checking if the user has released the mouse
if (draw && !mouseHeld)
{
finalMousePosition = mousePosition;
draw = false;
if (boxAnim.GetBool("Pressed") == true) //if the box draw button is pressed
{
boxDraw(originalMousePosition, finalMousePosition); //draws crate
}
}
}
void boxDraw(Vector3 start, Vector3 end)
{
//asigns world coordinates for the start and end mouse positions
worldStart = Camera.main.ScreenToWorldPoint(start);
worldEnd = Camera.main.ScreenToWorldPoint(end);
if (worldStart.y >= -3.2f && worldEnd.y >= -3.2f)
{
//determines the size of box to be drawn
boxSize.x = Mathf.Abs(worldStart.x - worldEnd.x);
boxSize.y = Mathf.Abs(worldStart.y - worldEnd.y);
//crate sprite is 175px wide, 175/50 = 3.5 (50px per unit) so the scale factor must be the size, divided by 3.5
boxScaleFactor.x = boxSize.x / 3.5f;
boxScaleFactor.y = boxSize.y / 3.5f;
//initial scale of the box is 1 (this isn't necessary but makes reading program easier)
boxScale.x = 1 * boxScaleFactor.x;
boxScale.y = 1 * boxScaleFactor.y;
//creates a new crate under the name newBox and alters its size
GameObject newBox = Instantiate(box, normalCoords(start, end), box.transform.rotation) as GameObject;
newBox.transform.localScale = boxScale;
}
}
Vector3 normalCoords(Vector3 start, Vector3 end)
{
//takes start and end coordinates as position coordinates and returns a world coordinate coordinate for the box
if(end.x > start.x)
{
start.x = start.x + (Mathf.Abs(start.x - end.x) / 2f);
}
else
{
start.x = start.x - (Mathf.Abs(start.x - end.x) / 2f);
}
if(end.y > start.y)
{
start.y = start.y + (Mathf.Abs(start.y - end.y) / 2f);
}
else
{
start.y = start.y - (Mathf.Abs(start.y - end.y) / 2f);
}
start = Camera.main.ScreenToWorldPoint(new Vector3(start.x, start.y, 0f));
return start;
}
In a similar manner, I want to be able to have a 'ramp' object be able to be created, so that the user can click and drag to determine the base width, then click again to determine the angle of elevation/ height, (the ramp will always be a right angled triangle.) The problem lies in that I want to have the ramp as a sprite I have created, rather than just a basic block colour. A single sprite however would only have a single angle of elevation, and no transform would be able to alter this (as far as I'm aware.) Obviously I don't want to have to create a different sprite for each angle, so is there anything I can do?
The solution I was thinking was if there was some sort of feature whereby I could alter the nodes of a vector image in the code, but I'm pretty sure this doesn't exist.
EDIT: Just to clarify this is a 2D environment, the code includes Vector3s just because that’s what I’m used to
You mention Sprite which is a 2D object (well, its actually very much alike a Quad which counts as 3D) but you reference full 3D in other parts of your question and in your code, which I think was confusing people, because creating a texture for a sprite is a very different problem. I am assuming you mentioned Sprite by mistake and you actually want a 3D object (Unity is 3D internally most of the time anyways), it can only have one side if you want
You can create 3D shapes from code no problems, although you do need to get familiar with the Mesh class, and mastering creating triangles on the fly takes some practice
Here's a couple of good starting points
https://docs.unity3d.com/Manual/Example-CreatingaBillboardPlane.html
https://docs.unity3d.com/ScriptReference/Mesh.html
I have a solution to part of the problem using meshes and a polygon collider. I now have a function that will create a right angled triangle with a given width and height and a collider in the shape of that triangle:
using UnityEngine;
using System.Collections;
public class createMesh : MonoBehaviour {
public float width = 5f;
public float height = 5f;
public PolygonCollider2D polyCollider;
void Start()
{
polyCollider = GetComponent<PolygonCollider2D>();
}
// Update is called once per frame
void Update () {
TriangleMesh(width, height);
}
void TriangleMesh(float width, float height)
{
MeshFilter mf = GetComponent<MeshFilter>();
Mesh mesh = new Mesh();
mf.mesh = mesh;
//Verticies
Vector3[] verticies = new Vector3[3]
{
new Vector3(0,0,0), new Vector3(width, 0, 0), new Vector3(0,
height, 0)
};
//Triangles
int[] tri = new int[3];
tri[0] = 0;
tri[1] = 2;
tri[2] = 1;
//normals
Vector3[] normals = new Vector3[3];
normals[0] = -Vector3.forward;
normals[1] = -Vector3.forward;
normals[2] = -Vector3.forward;
//UVs
Vector2[] uv = new Vector2[3];
uv[0] = new Vector2(0, 0);
uv[0] = new Vector2(1, 0);
uv[0] = new Vector2(0, 1);
//initialise
mesh.vertices = verticies;
mesh.triangles = tri;
mesh.normals = normals;
mesh.uv = uv;
//setting up collider
polyCollider.pathCount = 1;
Vector2[] path = new Vector2[3]
{
new Vector2(0,0), new Vector2(0, height), new Vector2(width, 0)
};
polyCollider.SetPath(0, path);
}
}
I just need to put this function into code very similar to my code for drawing a box so that the user can specify width and height.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class WallsTest : MonoBehaviour
{
// using a GameObject rather than a transform
public GameObject prefab;
public Vector3 wallsStartPosition;
public float width = 0;
public float height = 1;
public float length = 2;
public Camera wallsCamera;
public float wallsArea;
void Start()
{
wallsCamera.transform.position = new Vector3(wallsStartPosition.x, wallsStartPosition.y + 100, wallsStartPosition.z - 235);
BuildWalls();
}
private void Update()
{
}
void BuildWalls()
{
for (int i = -2; i < 2; i++)
{
GameObject go = Instantiate(prefab);
go.transform.parent = transform;
Vector3 scale = Vector3.one;
Vector3 adjustedPosition = wallsStartPosition;
float sign = Mathf.Sign(i);
if ((i * sign) % 2 == 0)
{
adjustedPosition.x += (length * sign) / 2;
scale.x = width;
scale.y = height;
scale.z *= length + width;
}
else
{
adjustedPosition.z += (length * sign) / 2;
scale.x *= length + width;
scale.y = height;
scale.z = width;
}
adjustedPosition.y += height / 2;
go.transform.localScale = scale;
go.transform.localPosition = adjustedPosition;
}
}
}
For example the length is 100 so the area will be 100x100 i think.
And i have the wallsStartPosition for example 250,0,250
Now i want inside the walls area to instantiate at random position number of objects. For example 50 cubes. But they should not be overlap each other and for example the minimum gap between each other should be 5. and maximum gap as fas it can be.
But i don't understand yet how to calculate the area and position of the walls and just instantiate random objects inside.
And this is the script for spawning gameobjects at random positions in given area. In this case the area is the terrain. But i want the area to be inside the walls i create in the first script. This script is attached to the same empty gameobject like the WallsTest script.
Another problem with the SpawnObjects script is that there is no any set for the gap between the objects and if the objects too good like scale 20,20,20 some of the objects that spawn on the edge half out of the terrain.
spawn
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpawnObjects : MonoBehaviour
{
public Terrain terrain;
public int numberOfObjects; // number of objects to place
private int currentObjects; // number of placed objects
public GameObject objectToPlace; // GameObject to place
private int terrainWidth; // terrain size (x)
private int terrainLength; // terrain size (z)
private int terrainPosX; // terrain position x
private int terrainPosZ; // terrain position z
void Start()
{
// terrain size x
terrainWidth = (int)terrain.terrainData.size.x;
// terrain size z
terrainLength = (int)terrain.terrainData.size.z;
// terrain x position
terrainPosX = (int)terrain.transform.position.x;
// terrain z position
terrainPosZ = (int)terrain.transform.position.z;
}
// Update is called once per frame
void Update()
{
// generate objects
if (currentObjects <= numberOfObjects)
{
// generate random x position
int posx = Random.Range(terrainPosX, terrainPosX + terrainWidth);
// generate random z position
int posz = Random.Range(terrainPosZ, terrainPosZ + terrainLength);
// get the terrain height at the random position
float posy = Terrain.activeTerrain.SampleHeight(new Vector3(posx, 0, posz));
// create new gameObject on random position
GameObject newObject = (GameObject)Instantiate(objectToPlace, new Vector3(posx, posy, posz), Quaternion.identity);
newObject.transform.localScale = new Vector3(20, 20, 20);
currentObjects += 1;
}
if (currentObjects == numberOfObjects)
{
Debug.Log("Generate objects complete!");
}
}
}
My technical knowledge is limited, but you'll have to either get the script to spawn these objects every [X] amount of position ("posX += 1.0f;", or something to that effect) so that they appear in a uniform manner, or, have the script record each new object's position and use that information to calculate space away from said objects to spawn another one.
In any case, depending on the end result you're aiming for, you'll have to write how these objects operate in a given space. For example, if you have a messy room as a scene, you can have one desk that would find space in the corner of two walls, one bed alongside one wall and rubbish spawning randomly.
You can use Physics.OverlapSphere to test if two objects are overlapping. This assumes that the objects both have colliders.
Once you test this condition and it evaluates to true, I would suggest simply moving the object that overlaps and testing it again against the other objects in the scene. You can get clever with this by only testing objects you think might be close to overlapping, or overlapping.
Alternatively you can just compare every object to every other object in the scene. This would not be ideal if you have a lot of objects, because doing so would be O(n^2) complexity.
In any case you can use this function to test overlapping. Good luck.
hint: if you make the collider bigger than the object, you can place items a minimum space apart. For example if you have a cube of localSize 1,1,1 - the colider can be 5,5,5 in size and Physics.OverlapSphere will return true if the colliders overlap, demanding that the cubes be a certain space apart from each other
I have a working [sort of] health bar that displays when an enemy is pursuing you. The only problem is that it shows up, off centered, at the enemy's feet. I would like the bar to be centered above the enemy's head.
I have an idea of where the problem is, but no idea how to fix it.
public float maxHealth;
public float curHealth;
public Texture2D healthBar;
private float left;
private float top;
private Vector2 playerScreen;
public Fighter player;
public Mob target;
public float healthPercent;
void Start ()
{
maxHealth = 10;
curHealth = maxHealth;
}
void Update ()
{
if(player.opponent != null) {
target = player.opponent.GetComponent<Mob>();
healthPercent = (float)target.health / (float)target.maxHealth;
} else {
target = null;
healthPercent = 0;
}
playerScreen = Camera.main.WorldToScreenPoint(target.transform.position);
left = playerScreen.x; //pretty sure right here
top = (Screen.height - playerScreen.y); //is the issue
}
void OnGUI()
{
if (target != null) {
GUI.DrawTexture(new Rect(left, top, (50 * healthPercent), 5), healthBar);
}
}
WorldToScreenPoint gives you the WorldPoint where your model has its origin, i guess thats at its feet. So you want to add the height to it:
Vector3 healthBarWorldPosition = target.transform.position + new Vector3(0.0f, target.height, 0.0f);
healthBarScreenPosition = Camera.main.WorldToScreenPoint(healthBarWorldPosition);
where target.height is the height of the model(maybe a bit more)
This should give you the correct height. For the centered part:
left = playerScreen.x;
says that the Rectangle has its left end at the center of your model. Thats why its off center. You have to substract halt the pixel size of your healthbar to have it centered.
private int healthBarWidth = 50;
private int healthBarHeight = 5;
...
left = healthBarScreenPosition.x - (healthBarWidth / 2);
top = healthBarScreenPosition.y + (healthBarHeight / 2);
The same holds true for the height, you just have to add instead of substract because ScreenPoints count from bottom to top and the Rect counts from top to bottom.
edit: ha, i guess i am your personal tutor today ;)