Keep a particlesystems transform and cone radius relative to its parent - c#

I have a "Lift". While being in the game, you walk into the particle system and get moved up in the air (on y).
So the particle system is a child of the cube / the lift. So when scaling the cube, I don't want to change the settings of the particle system. It should scale itself on its own.
When the cube got the y position on 5 and a height / scaling on y of 10, the particle system should place itself down at the bottom.
As you can see, I want it being full automatic.
So, when heading into the code I got this
[SerializeField]
ParticleSystem liftParticles;
private void Start()
{
Vector3 objectScale = transform.localScale; // cube scaling
Vector3 particlePos = liftParticles.transform.position; // temp position
particlePos.y = (particlePos.y - objectScale.y) / 2; // move down on y
liftParticles.transform.position = particlePos; // set particle position
float transformScalingX = objectScale.x; // x scaling of the cube
float transformScalingZ = objectScale.z; // z scaling of the cube
var shape = liftParticles.shape; // set the cone radius now
shape.radius = transformScalingX > transformScalingZ ? transformScalingX : transformScalingZ;
liftParticles.shape = shape;
}
I want to go with the following example as mentioned above..
The cube got a scaling of (3,10,3) and its position is (0,5,0)
my current calculation particlePos.y returns a value of -0,75 but it has to be -0,5.
So do I have an error in my code? (yes obviously I do ...)
The second problem is, how do I change the radius of the particlesystem? When trying to reference the radius of the cone, it says I can't change it, it is readonly.
Is it? I hope I can change this somehow ...
Edit:
Obviously, the particlesystem just has to be always on -0,5f on y when having a scaling of (1,1,1). No need for a calculation anymore.
But I still need to change the radius of the shape and set the lifetime of the particles relative to the height of the lift. Means
private void Start()
{
Vector3 liftScale = transform.localScale; // Liftscaling
var shape = liftParticles.shape; // temp shape
shape.radius = liftScale.x > liftScale.z ? liftScale.x : liftScale.z; // set radius
liftParticles.shape = shape; // assign the temp shape to the real shape
liftParticles.main.startLifetime = ; // set the liftetime of the particles relative to its parent height on y
}

As I understand you made the particle system child of the lift (a cube) so it can move together. In case you just want that both of them move together, but they scale independently, you should consider to use an Empty GameObject as a parent.
You can placed this Empty GameObject in the middle of the Cube (your lift) and then make the lift and the particle filter children of that Empty GameObject. Then move the Empty GameObject instead of the lift, and the children will move as well.
About modifying the radio, try this script
GameObject myParticleGenerator;
ParticleSystem.ShapeModule pShape;
pShape = yParticleGenerator.GetComponent<ParticleSystem>().shape;
pShape.radius = 4.0f;

Related

Make instantiated object visible on canvas

I have a canvas, and I'd like an instantiated object to be positioned and visible on it.
In Scene view, it looks like it's right in the middle of the canvas. In Game view, it's not visible.
GameObject prefab = Resources.Load<GameObject>("cereal");
Vector3 point = new Vector3(0, 0, 10);
GameObject clone = Instantiate(prefab, point, Quaternion.identity) as GameObject;
clone.transform.parent = canvas.transform;
clone.transform.localScale = new Vector3(100f, 100f, 100f);
What am I doing wrong here?
It depends on what render mode of canvas you are using.
Canvas has 3 rendering modes Screen Space - Overlay, Screen Space - Camera and World Space
For Screen Space - Overlay, if you want to show an object in the canvas rect you should use Camera.ScreenToWorldPoint() to convert the screen position to the world, but you must know where on the screen you want to see the object. Screen position it's position, where x in range 0 - Screen.width, y in range 0 - Screen.height. You must set the distance to the camera yourself.
Example:
//Get the center of the screen;
var centerOfTheScreen = new Vector3(Screen.width/2, Screen.height/2);
var camera = Camera.main;
var distance = camera.transform.forward * 10;// here you can set prefered distance;
var objectPosition = camera.ScreenToWorldPoint(centerOfTheScreen) + distance;
var instance = Instanitate(prefab, objectPosition, Quaternion.identity);
For Screen Space - Camera you can use world position of the camera. In this mode no need to convert screen to world position, camera position will be enough. You can use distance calculation form the previous mode (var distance = camera.transform.forward * 10;) and add it to the camera position. It`s will looks like:
var camera = Camera.main;
var distance = camera.transform.forward * 10;// here you can set prefered distance;
var objectPosition = camera.transform.position + distance;
var instance = Instanitate(prefab, objectPosition, Quaternion.identity);
For World Space you can directly use world position of the canvas gameobject, but, I think, it`s not you case. Just use canvas position instead camera.
Example:
//This code snippet create object benind canvas. If you want create object on canvas - remove distance
var distance = canvas.transform.forward * 10;// here you can set prefered distance;
var objectPosition = canvas.transform.position + distance;
var instance = Instanitate(prefab, objectPosition, Quaternion.identity);
UPD: If you want to make an object the child of a canvas transform, you can use instance.transform.SetParent(canvas.transform, false) and after that use the calculation described above. For the World Space canvas it will be even easier.

how to find a position of an Object from World Space and convert to Canvas UI with render mode : Screen Space - Camera in Unity 2d?

I am working in a Game which is pretty similar to Mario. So when player touches the coin object in World Space, I need to animate by moving that coin object to Coin meter, when the render mode of Canvas is Screen Space - Overlay, I can get the sprite object position easily with below code
CoinSprite Code
GameObject coinCanvasObject = Instantiate(prefab, canvas.transform);//Instantiate coin inside Canvas view
coinCanvasObject.transform.position = Camera.main.WorldToScreenPoint(coinSpriteObject.transform.position);//getting coin position from World Space and convert to Screen Space and set to coinCanvasobject position
AnimateCoin animate = coinCanvasObject.GetComponent<AnimateCoin>();
animate.animateCoin(coinSpriteObject.transform.position);
coinSpriteObject.SetActive(false);
AnimateCoin
public class AnimateCoin : MonoBehaviour
{
private float speed = 0f;
private bool isSpawn = false;
private Vector3 screenPos;
public void animateCoin(Vector3 screenPosTemp, Camera cam, Canvas canvas)
{
screenPos = Camera.main.WorldToScreenPoint(screenPosTemp);
isSpawn = true;
}
private void Update()
{
if (isSpawn)
{
speed += 0.025f;
transform.position = Vector3.Lerp(screenPos, targetObject.transform.position, speed);
if (Vector3.Distance(transform.position, targetObject.transform.position) <= 0)
{
StartCoroutine(deActivateCoin());
}
}
}
private IEnumerator deActivateCoin()
{
isSpawn = false;
yield return new WaitForSeconds(0.2f);
gameObject.SetActive(false);
}
}
Since I need to bring particle effect into Canvas view, I am changing the Canvas render mode to Screen Space - Camera.
When I change the Canvas to this render mode I could not get the exact sprite object position to trail the coin effect.
Hope this helps:
public Camera cam; // Camera containing the canvas
public Transform target; // object in the 3D World
public RectTransform icon; // icon to place in the canvas
public Canvas canvas; // canvas with "Render mode: Screen Space - Camera"
void Update()
{
Vector3 screenPos = cam.WorldToScreenPoint(target.position);
float h = Screen.height;
float w = Screen.width;
float x = screenPos.x - (w / 2);
float y = screenPos.y - (h / 2);
float s = canvas.scaleFactor;
icon.anchoredPosition = new Vector2(x, y) / s;
}
PD: It worked perfectly for me in a 2D video game, I didn't test it in a 3D game, but I think it should work too.
I rewrote my previous solution because it might not work correctly on some devices with non-standard resolutions.
This code should always work.
uiObject.anchoredPosition = GetUIScreenPosition(myPin.position, cam3d, uiObject.anchorMin);
public static Vector2 GetUIScreenPosition(Vector3 obj3dPosition, Camera cam3d, Vector2 anchor)
{
Vector2 rootScreen = _rootCanvasRect.sizeDelta;
Vector3 screenPos = cam3d.WorldToViewportPoint(obj3dPosition);
return (rootScreen * screenPos) - (rootScreen * anchor);
}
We take the sizeDelta of our UI Canvas, because it may differ from the screen resolution of the device.
Then we cast the WorldToViewportPoint from our 3d camera to get the relative position on the screen in the format from 0 to 1 by X and Y.
With anchors in the lower left corner ((0,0)(0,0)) this is our final anchoredPosition. However with anchors for example in the center ((0.5,0.5)(0.5,0.5)) we need to adjust the positions by subtracting half the canvas size.
In this example, we will get an unpredictable result when using different min and max anchors in the final object. For example ((0,25,0.25)(0.75,0.75)). But I sincerely doubt that you really need such anchors on an object with a dynamic position depending on the 3d object.

Object Not Looking at Other Object at Right Angle in Unity3D

I'm working on a prototype of a tower defense game and I've encountered a problem with the rotation of a turret. I made it so that every turret must have a rotator part which rotates horizontally and holds the main turret body with the cannon which rotates vertically. I came up with a simple script to this but it only seems to work for the rotator and not for the cannon, at least not the way it should.
Here is the code from the script:
void Update () {
if (target != null) {
Vector3 tempRotatorRotation = rotator.transform.localEulerAngles;
rotator.transform.LookAt (target.transform);
rotator.transform.localEulerAngles = new Vector3 (tempRotatorRotation.x, rotator.transform.localEulerAngles.y, tempRotatorRotation.z);
Vector3 tempCannonRotation = cannon.transform.localEulerAngles;
cannon.transform.LookAt (target.transform);
cannon.transform.localEulerAngles = new Vector3 (cannon.transform.localEulerAngles.x, tempCannonRotation.y, tempCannonRotation.z);
}
}
And here is an image of how this turns out. The rotator is rotated perfectly, but as you can see the cannon is looking down for some reason.
(Blue is the pedestal which doesn't move. Green is rotator. Red is turret body. Light blue is cannon)
The origin of the cannon 3D model is set almost at the start of it.
Here is the screenshot of the canon selected showing it's axis and transform data
forward to unity is the blue line, which in your diagram is facing up. try this
crate empty, attach to turret so it rotates, make sure blue line(z axis) is facing your forward direction, you can do this manualy by rotating. then place your barrel as a child of that object, and point that object at target.
ive had to do this several times with blender models, since blender uses the z axis as its vertical axis not its depth axis like unity.
-turret_test
-turret_test_pedestal
-turret_test_rotater
-turret_test_turret
-AIM(new empty, orient the proper direction then add child)
-turret_test_cannon
Your cannon's barrel points in its forward direction, so all you need to use is cannon.transform.LookAt (target.transform, cannon.transform.up);
void Update () {
if (target != null) {
/* rotator code here */
// Remember which way the top of the cannon faces.
Vector3 cannonUpDirection = cannon.transform.up;
// point the cannon directly at the target
cannon.transform.LookAt (target.transform, cannonUpDirection);
}
}
If the rotator isn't pointing at/above/below the target, then you have to figure out how much to rotate the canon upwards/downwards from the horizontal, then point it in the same direction as the rotator and then do that:
void Update () {
if (target != null) {
/* rotator code here */
// Remember which way the top of the cannon faces.
Vector3 cannonUpDirection = cannon.transform.up;
// point the cannon directly at the target
cannon.transform.LookAt (target.transform, cannonUpDirection);
// Find global direction for looking straight ahead above/below the target
Vector3 sameYDirection = Vector3.Scale(
cannon.transform.forward,
new Vector3(1f,0f,1f)
);
// Convert that to local
Vector3 sameYDirectionLocal = cannon.transform
.InverseTransformDirection(sameYDirection);
// get rotation for moving from looking ahead to looking direct
Quaternion lookTowards = Quaternion.FromToRotation(
sameYDirectionLocal,
Vector3.forward
);
// lookTowards is a locally-vertical rotation from horizontal to the target,
// given some top side of the cannon.
// Clamp lookTowards here if you have a max rotation speed.
// Face cannon in same direction of rotator;
cannon.transform.rotation = Quaternion.LookRotation(
rotator.forward, cannonUpDirection);
// Use lookTowards to look down from that position.
cannon.transform.rotation *= lookTowards;
}
}
Turns out Unity is broken and when I re-imported the model all the axis have changed and instead of the canon turning in the X axis up and down now it was in the Y axis while everything else rotates horizontally in Y.
Also in the calculation I had to add + 90 to both equations. This will make no sense now since it shows both are changing in Y axis but one is rotating horizontally and the other vertically.
if (target != null) {
Vector3 tempRotatorRotation = rotator.transform.localEulerAngles;
rotator.transform.LookAt (target.transform);
rotator.transform.localEulerAngles = new Vector3 (tempRotatorRotation.x, rotator.transform.localEulerAngles.y + 90, tempRotatorRotation.z);
Vector3 tempCanonRotation = canon.transform.localEulerAngles;
canon.transform.LookAt (target.transform);
canon.transform.localEulerAngles = new Vector3 (tempCanonRotation.x, canon.transform.localEulerAngles.y + 90, tempCanonRotation.z);
}

How can I move some object in circle around another object?

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TargetBehaviour : MonoBehaviour
{
// Add this script to Cube(2)
[Header("Add your turret")]
public GameObject Turret;//to get the position in worldspace to which this gameObject will rotate around.
[Header("The axis by which it will rotate around")]
public Vector3 axis;//by which axis it will rotate. x,y or z.
[Header("Angle covered per update")]
public float angle; //or the speed of rotation.
public float upperLimit, lowerLimit, delay;// upperLimit & lowerLimit: heighest & lowest height;
private float height, prevHeight, time;//height:height it is trying to reach(randomly generated); prevHeight:stores last value of height;delay in radomness;
// Update is called once per frame
void Update()
{
//Gets the position of your 'Turret' and rotates this gameObject around it by the 'axis' provided at speed 'angle' in degrees per update
transform.RotateAround(Turret.transform.position, axis, angle);
time += Time.deltaTime;
//Sets value of 'height' randomly within 'upperLimit' & 'lowerLimit' after delay
if (time > delay)
{
prevHeight = height;
height = Random.Range(lowerLimit, upperLimit);
time = 0;
}
//Mathf.Lerp changes height from 'prevHeight' to 'height' gradually (smooth transition)
transform.position = new Vector3(transform.position.x, Mathf.Lerp(prevHeight, height, time), transform.position.z);
}
}
In general it's working the problem is for example if I set the axis x,y,z to 1,1,1 on the variable: axis
And Angle set to 1 Upper limit to 50 Lower limit to 2 and delay to 2.
Then the object is making a circle around the other object but sometimes when the object is getting higher the most he get higher the object making a bigger circle and then when the object is getting lower the circle is smaller.
How can I make it to keep the circle radius static ?
The main goal is to move the object in circles around another object in random highs limits for example 2 and 50 but I want to keep the same radius all the time. Now the radius is changing by depending on the height.
As you are constantly moving the object upwards, if you want the radius of rotation to remain constant then the axis of rotation must veertical ie - Vector3.up or new Vector3(0, 1, 0)

Rotation of a Sprite Around Its Pivot

I have a class below that I attach to a object in order to make it rotate around its pivot. I sent the pivot of the sprite via the inspector.
This works exactly how I want it too, BUT the issue I am having is that whenever I touch and drag it, and then touch and drag it again, it snaps to a new position.
What I would like for it to do is, when it is rotated and then rotated again, the sprite stays in its same rotation and not snap to a new position and I would like the angle of the sprite to be reset to 0. The next then is that I want the angle to continually rotate. So if I rotate it in the positive direction, the angle should keep increasing in the positive direction and not change..Such as 0---> 360 ----> 720 -----> etc, etc. And then when the mouse is released, the sprite stays in the same position but the angle is now set back to 0. And then when clicked again to rotate, it rotates from that exact position.
Here is my code thus far which works well for rotating, but I would like to modify it to achieve the above scenario. Any help with this?
public class Steering : MonoBehaviour {
float prevAngle,wheelAngle,wheelNewAngle = 0;
public SpriteRenderer sprite;
void Start () {
}
void Update () {
}
public float GetAngle(){
return wheelAngle;
}
void OnMouseDrag(){
Vector3 mouse_pos = Input.mousePosition;
Vector3 player_pos = Camera.main.WorldToScreenPoint(this.transform.position);
mouse_pos.x = mouse_pos.x - player_pos.x;
mouse_pos.y = mouse_pos.y - player_pos.y;
wheelNewAngle = Mathf.Atan2 (mouse_pos.y, mouse_pos.x) * Mathf.Rad2Deg;
if (Input.mousePosition.x > sprite.bounds.center.x) {
wheelAngle += wheelNewAngle - prevAngle;
} else {
wheelAngle -= wheelNewAngle - prevAngle;
}
this.transform.rotation = Quaternion.Euler (new Vector3(0, 0, wheelAngle));
Debug.Log (wheelAngle);
prevAngle = wheelNewAngle;
}
void OnMouseUp(){
prevAngle = wheelNewAngle;
wheelAngle = 0;
}
}
By angle of the sprite, do you mean the rotation? I'm not sure how the position is changing if there's nothing in your code doing that. Does it always move to the same position? I'm having a little trouble visualizing how your system is supposed to look but I hope this helps.
It looks like you might want to store the previous mouse position so you can get the relative amount to rotate each frame.
At the top:
Vector3 prevMousePos = Vector3.zero;
This method will help get the position when the player pressed:
void OnMouseDown(){
prevMousePos = Input.mousePosition;
}
Then in OnMouseDrag() get the difference between the two mouse positions to get the relative position (if you moved the mouse left, right, up, or down since pressing):
Vector3 mouseDiff = Input.mousePosition - prevMousePos;
With this it will use the relative mouse position after pressing instead of the current one, which should smooth things out.

Categories

Resources