I have cars inside a Vertical Layout, they need to reach the finish line but they're moving up and down when I scroll up and down, I want the cars to only move in one direction on the Y-axis, how can I achieve this?
enter image description here
your text
public Transform\[\] startLines;
public Transform\[\] finishLines;
public float speed = 10f;
private int currentRectangle = 0;
public void MoveCar()
{
if (currentRectangle >= startLines.Length)
{
return;
}
StartCoroutine(MoveToEndPosition());
}
private IEnumerator MoveToEndPosition()
{
for (int i = 0; i < startLines.Length; i++)
{
Transform startLine = startLines[currentRectangle];
Transform finishLine = finishLines[currentRectangle];
Vector3 endPosition = finishLine.position;
while (transform.position != endPosition)
{
transform.position = Vector3.MoveTowards(transform.position , endPosition, speed * Time.deltaTime);
yield return null;
}
currentRectangle++;
if (currentRectangle < startLines.Length)
{
transform.position = startLines[currentRectangle].position;
}
}
}
I am working on a unity game and
I have a gameobject i need to constantly rotate towards a target angle
And it needs to take the shortest way there
I have tried to use lerp co-routines to add/subbtract it to the angle but when i use it to quickly it gets stuck in weird positions
Transform target;
float speed;
//The angle to constantly rotate torwards
float yRotation = 120f;
private int dick;
void Start()
{
}
void Update()
{
if (Input.GetKeyDown(KeyCode.D))
{
if (dick < 3)
{
dick += 1;
}
else
{
dick = 1;
}
}
else if (Input.GetKeyDown(KeyCode.A))
{
if (dick >0 )
{
dick -= 1;
}
else
{
dick = 3;
}
}
if (dick == 1)
{
yRotation = 0;
}
else if (dick == 2)
{
yRotation = 120;
}
else if (dick == 3)
{
yRotation = 240;
}
As you might see there is some parts from the old code
The reason i use this kind of gear system is so that it cant get stuck in weird positions but i am not sure how to constantly rotate it to that target angle
Two things you can try :
In the Update, every time calculate the direction between you and the target, then calcultate the Angle, and then use Quaternion.AngleAxis() or simply Quaternion.RotateTowards()
You can also simply use Transform.LookAt(yourTarger.transform)
This is a follow-up question from How to make individual anchor points of bezier continuous or non-continuous. Please refer to it for the relevant code in the accepted answer (please note that I did this to keep this question clean since the related code is quite lengthy).
I am trying to achieve the following:
Make the bezier curve handles/control points selectable in such a way that the properties (for example continuity) for an individual handle are displayed in the inspector window when selected. Please note I'd like this to be done without making creating game objects for the handles/ control points
Retain a single method that handles the movement of each point instead of having separate methods for the movement of each point.
Am I late to the party?
Make the bezier curve handles/control points selectable in such a way that the properties (for example continuity) for an individual handle are displayed in the inspector window when selected. Please note I'd like this to be done without making creating game objects for the handles/ control points
I like #jour's solution in general, except 1 thing: with Handles.Button you have to click a point to select it and then you click and drag to move the control point.
I propose a different approach. Using the same Handles.FreeMoveHandle, but having a variable to remember the id of the last clicked handle, so I can identify it.
Usually, a built in Handle won't give you more info than what it is designed to do. FreeMoveHandle, for example, returns the delta of its translation and that's all. The problem is: You want to capture a simple click, but if you just clicked and didn't drag, the return value is Vector3.zero and it is just the same as if you didn't do a click at all.
Good news: among the overloads of any Handle, there are some calls with an argument named controlID - it is an identifier for each interactable GUI object. If you supress it, the Engine will choose one and you will never know what. But if you pass an int, that value will be the id of the handle. But if I pass an int and it happen to conflict with any of the other ids i don't see? Well, you can call GUIUtility.GetControlID to get a safe id.
Then, it is straightforward. If the id of a Handle is the same as EditorGUIUtility.hotControl (that is, the control that got clicked or have the keyboard focus), then I save the index of this point in selectedControlPointId and use it to display a custom property in Inspector.
Retain a single method that handles the movement of each point instead of having separate methods for the movement of each point.
Hmm... Here become the controversy. If I understood it correctly, you want a single code to draw both nodes and tangents. The thing is: this two things are different in nature. Of course, if you keep it plain and simple, they are bot movable points in scene. But, when you introduces things like constraints (the continuity, or smooth) and selection, they become different beasts, with different logic. Even if you want to make ControlPoint a struct (like i did now) and pass it over as a whole, you'd still need to point what component you are aiming to update, so the constraints will apply to the others - you will always need a "master" field to avoid circular updates (you change tangentBack, and that makes tangentFront to update, that trigger tangentBack to update again and so on).
That's why, even though I reorganized somehow the ControlPoint methods and made it a struct, I can't make a single logic to draw both nodes and tangents.
Here are some codes. I started over from the codes on my answer in the previous question.
ControlPoint.cs
using System;
using UnityEngine;
[Serializable]
public struct ControlPoint
{
public Vector2 position;
public Vector2 tangentBack;
public Vector2 tangentFront;
public bool smooth;
static public ControlPoint MovePosition(ControlPoint pt, Vector2 newPos)
{
var newPt = pt;
newPt.position = newPos;
return newPt;
}
static public ControlPoint MoveTangentBack(ControlPoint pt, Vector2 newTanBack)
{
var newPt = pt;
newPt.tangentBack = newTanBack;
if (pt.smooth) newPt.tangentFront = pt.tangentFront.magnitude * -newTanBack.normalized;
return newPt;
}
static public ControlPoint MoveTangentFront(ControlPoint pt, Vector2 newTanFront)
{
var newPt = pt;
newPt.tangentFront = newTanFront;
if (pt.smooth) newPt.tangentBack = pt.tangentBack.magnitude * -newTanFront.normalized;
return newPt;
}
static public ControlPoint WithSmooth(ControlPoint pt, bool smooth)
{
var newPt = pt;
if (smooth != pt.smooth) newPt.tangentBack = -pt.tangentFront;
return newPt;
}
public ControlPoint(Vector2 position, Vector2 tanBack, Vector2 tanFront, bool smooth = false)
{
this.position = position;
this.tangentBack = tanBack;
this.tangentFront = tanFront;
this.smooth = smooth;
}
}
I removed ControlPointDrawer, so the other propertis you added to it won't be hidden in inspector.
Path.cs
using System;
using UnityEngine;
using System.Collections.Generic;
[Serializable]
public class Path
{
[SerializeField] List<ControlPoint> _points;
[SerializeField] bool _loop = false;
public Path(Vector2 position)
{
_points = new List<ControlPoint>
{
new ControlPoint(position, -Vector2.one, Vector2.one),
new ControlPoint(position + Vector2.right, -Vector2.one, Vector2.one)
};
}
public bool loop { get { return _loop; } set { _loop = value; } }
public ControlPoint this[int i]
{
get { return _points[(_loop && i == _points.Count) ? 0 : i]; }
set { _points[(_loop && i == _points.Count) ? 0 : i] = value; }
}
public int NumPoints { get { return _points.Count; } }
public int NumSegments { get { return _points.Count - (_loop ? 0 : 1); } }
public ControlPoint InsertPoint(int i, Vector2 position)
{
_points.Insert(i, new ControlPoint(position, -Vector2.one, Vector2.one));
return this[i];
}
public ControlPoint RemovePoint(int i)
{
var item = this[i];
_points.RemoveAt(i);
return item;
}
public Vector2[] GetBezierPointsInSegment(int i)
{
var pointBack = this[i];
var pointFront = this[i + 1];
return new Vector2[4]
{
pointBack.position,
pointBack.position + pointBack.tangentFront,
pointFront.position + pointFront.tangentBack,
pointFront.position
};
}
public ControlPoint MovePoint(int i, Vector2 position)
{
this[i] = ControlPoint.MovePosition(this[i], position);
return this[i];
}
public ControlPoint MoveTangentBack(int i, Vector2 position)
{
this[i] = ControlPoint.MoveTangentBack(this[i], position);
return this[i];
}
public ControlPoint MoveTangentFront(int i, Vector2 position)
{
this[i] = ControlPoint.MoveTangentFront(this[i], position);
return this[i];
}
}
PathCreator.cs is the same
PathCreatorEditor.cs
using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(PathCreator))]
public class PathCreatorEditor : Editor
{
PathCreator creator;
Path path;
SerializedProperty property;
static int selectedControlPointId = -1;
public override void OnInspectorGUI()
{
serializedObject.Update();
var loopProp = property.FindPropertyRelative("_loop");
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(loopProp);
var ptsProp = property.FindPropertyRelative("_points");
var msg = "Total points in path: " + ptsProp.arraySize + "\n";
if (selectedControlPointId >= 0 && ptsProp.arraySize > 0)
{
EditorGUILayout.HelpBox(msg + "Selected control point: " + selectedControlPointId, MessageType.Info);
EditorGUILayout.Separator();
EditorGUILayout.PropertyField(ptsProp.GetArrayElementAtIndex(selectedControlPointId), true);
}
else
{
EditorGUILayout.HelpBox(msg + "No control points selected", MessageType.Info);
}
if (EditorGUI.EndChangeCheck()) serializedObject.ApplyModifiedProperties();
}
void OnSceneGUI()
{
Input();
Draw();
}
void Input()
{
Event guiEvent = Event.current;
Vector2 mousePos = HandleUtility.GUIPointToWorldRay(guiEvent.mousePosition).origin;
mousePos = creator.transform.InverseTransformPoint(mousePos);
if (guiEvent.type == EventType.MouseDown && guiEvent.button == 0 && guiEvent.shift)
{
Undo.RecordObject(creator, "Insert point");
path.InsertPoint(path.NumPoints, mousePos);
}
else if (guiEvent.type == EventType.MouseDown && guiEvent.button == 0 && guiEvent.control)
{
for (int i = 0; i < path.NumPoints; i++)
{
if (Vector2.Distance(mousePos, path[i].position) <= .25f)
{
Undo.RecordObject(creator, "Remove point");
path.RemovePoint(i);
break;
}
}
}
}
void Draw()
{
Handles.matrix = creator.transform.localToWorldMatrix;
var rot = Quaternion.Inverse(creator.transform.rotation) * Tools.handleRotation;
var snap = Vector2.zero;
Handles.CapFunction cap = Handles.DotHandleCap;
for (int i = 0; i < path.NumPoints; i++)
{
float size;
var pos = path[i].position;
size = HandleUtility.GetHandleSize(pos) * .05f;
Handles.Label(pos, i.ToString());
Handles.color = i == selectedControlPointId ? Handles.selectedColor : Color.red;
int ctrlId = GUIUtility.GetControlID(FocusType.Passive);
Vector2 newPos = Handles.FreeMoveHandle(ctrlId, pos, rot, size, snap, cap);
if (ctrlId == EditorGUIUtility.hotControl) selectedControlPointId = i;
if (pos != newPos)
{
Undo.RecordObject(creator, "Move point position");
path.MovePoint(i, newPos);
}
pos = newPos;
Handles.color = Color.black;
if (path.loop || i != 0)
{
var tanBack = pos + path[i].tangentBack;
Handles.DrawLine(pos, tanBack);
size = HandleUtility.GetHandleSize(tanBack) * .03f;
Vector2 newTanBack = Handles.FreeMoveHandle(tanBack, rot, size, snap, cap);
if (tanBack != newTanBack)
{
Undo.RecordObject(creator, "Move point tangent");
path.MoveTangentBack(i, newTanBack - pos);
}
}
if (path.loop || i != path.NumPoints - 1)
{
var tanFront = pos + path[i].tangentFront;
Handles.DrawLine(pos, tanFront);
size = HandleUtility.GetHandleSize(tanFront) * .03f;
Vector2 newTanFront = Handles.FreeMoveHandle(tanFront, rot, size, snap, cap);
if (tanFront != newTanFront)
{
Undo.RecordObject(creator, "Move point tangent");
path.MoveTangentFront(i, newTanFront - pos);
}
}
}
Repaint();
}
[DrawGizmo(GizmoType.Selected | GizmoType.NonSelected | GizmoType.Pickable)]
static void DrawGizmo(PathCreator creator, GizmoType gizmoType)
{
Gizmos.matrix = creator.transform.localToWorldMatrix;
var path = creator.path;
for (int i = 0; i < path.NumSegments; i++)
{
Vector2[] points = path.GetBezierPointsInSegment(i);
var pts = Handles.MakeBezierPoints(points[0], points[3], points[1], points[2], 30);
Gizmos.color = Color.green;
for (int j = 0; j < pts.Length - 1; j++)
{
Gizmos.DrawLine(pts[j], pts[j + 1]);
}
}
}
void OnEnable()
{
creator = (PathCreator)target;
path = creator.path ?? creator.CreatePath();
property = serializedObject.FindProperty("path");
}
}
Note: I moved the drawing of the Bezier line from OnSceneGui to DrawGizmo, so the green line will be visible even when the object is not selected, and it will be pickable in the Scene Editor, for having it selected.
Lastly, I would suggest some further development of this scripts. It wouldn't be very hard to make multiple point selection possible. Maybe making the default handles (like position and rotation) to be applicable individually on points. Or changing the method for creating and deleting points to something move intuitive like double-clicking or dragging the path line. Or even a custom toolbar to smart point manipulation, like align, distribute, sculpt... Different constraints, like smooth, symmetric, cusp or straight...
Not entirely sure if I understood the question, but
Make the bezier curve handles/control points selectable in such a way
that the properties (for example continuity) for an individual handle
are displayed in the inspector window when selected. Please note I'd
like this to be done without making creating game objects for the
handles/ control points
You need to use OnSceneGUI option to be able to select the handles, and everytime you select a new point just store its value.
private void OnSceneGUI ()
{
spline = target as Path;
handleTransform = spline.transform;
handleRotation = Tools.pivotRotation == PivotRotation.Local
? handleTransform.rotation : Quaternion.identity;
Vector3 p0 = ShowPoint (0);
Color gg = Color.gray;
gg.a = 0.25f;
for (int i = 1; i < spline.ControlPointCount; i += 3)
{
Vector3 p1 = ShowPoint (i);
Vector3 p2 = ShowPoint (i + 1);
Vector3 p3 = ShowPoint (i + 2);
Handles.color = gg;
Handles.DrawLine (p0, p1);
Handles.DrawLine (p2, p3);
Handles.DrawBezier (p0, p3, p1, p2, Color.white, null, 2f);
p0 = p3;
}
ShowDirections ();
}
private Vector3 ShowPoint (int index)
{
Vector3 point = handleTransform.TransformPoint (spline.Points[index]);
float size = HandleUtility.GetHandleSize (point);
if (index == 0)
{
size *= 2f;
}
Handles.color = modeColors[(int) spline.GetControlPointMode (index)];
if (Handles.Button (point, handleRotation, size * handleSize, size * pickSize, Handles.DotCap))
{
selectedIndex = index;
Repaint ();
}
if (selectedIndex == index)
{
EditorGUI.BeginChangeCheck ();
point = Handles.DoPositionHandle (point, handleRotation);
if (EditorGUI.EndChangeCheck ())
{
Undo.RecordObject (spline, "Move Point");
EditorUtility.SetDirty (spline);
spline.SetControlPoint (handleTransform.InverseTransformPoint (point), index);
}
}
return point;
}
and for showing the point on inspector GUI
private void DrawSelectedPointInspector ()
{
GUILayout.Label ("Selected Point");
EditorGUI.BeginChangeCheck ();
Vector3 point = EditorGUILayout.Vector3Field ("Position", spline.Points[selectedIndex]);
if (EditorGUI.EndChangeCheck ())
{
Undo.RecordObject (spline, "Move Point");
EditorUtility.SetDirty (spline);
spline.SetControlPoint (point, selectedIndex);
}
EditorGUI.BeginChangeCheck ();
BezierControlPointModes mode = (BezierControlPointModes) EditorGUILayout.EnumPopup ("Mode", spline.GetControlPointMode (selectedIndex));
if (EditorGUI.EndChangeCheck ())
{
Undo.RecordObject (spline, "Change Point Mode");
spline.SetControlPointMode (selectedIndex, mode);
EditorUtility.SetDirty (spline);
}
}
and call this on the InspectorGUI
if (selectedIndex >= 0 && selectedIndex < spline.points.Count)
{
DrawSelectedPointInspector ();
}
Retain a single method that handles the movement of each point instead
of having separate methods for the movement of each point.
For this you just need to retain the
I'm creating a script that allows objects to be faded considering an interval of time.
The script works very well, however when an object is faded, it is still very visible in the inspector AND in the build.
Could someone explain me why and how to make an object completely invisible?
(I know I can "enable" the object, but isn't it using more resources than fading? I don't know :( )
Here is the code if I've made any mistake (code inspired from a topic in unity's forum)
// Update is called once per frame
void Update ()
{
MyTime = Time.deltaTime;
AccumulatedTime += MyTime;
if (this.name == "Cube (1)")
{
Debug.Log(AccumulatedTime);
}
if (SwitchVisibility == 1)
{
if (AccumulatedTime >= Interval && AccumulatedTime<= 2*Interval || AccumulatedTime >= 3*Interval && AccumulatedTime<= 4*Interval)
{
StartCoroutine(FadeTo(0.0f, 1.0f));
SwitchVisibility = 0;
}
}
if (SwitchVisibility == 0)
{
if (AccumulatedTime >= 0 && AccumulatedTime <= Interval || AccumulatedTime >= 2*Interval && AccumulatedTime <= 3*Interval)
{
StartCoroutine(FadeTo(1.0f, 1.0f));
SwitchVisibility = 1;
}
}
if (AccumulatedTime >= Interval * 4.5f)
{
AccumulatedTime = 0;
}
}
IEnumerator FadeTo(float aValue, float aTime)
{
float alpha = MyRenderer.material.color.a;
for (float t = 0.0f; t < 1.0f; t += Time.deltaTime / aTime)
{
OriginalColor.a = Mathf.Lerp(alpha, aValue, t);
Color newColor = OriginalColor;
MyRenderer.material.color = newColor;
yield return null;
}
}
Here is what objects look like:
Assuming your code is fine, the problem very likely is from your Material settings. This changed in Unity 5. To change the alpha of a Mesh Renderer, you must also change the Material's Rendering Mode from Opaque(default) to Fade.
Transparent Mode is also fine but will will not be completely transparent and will result to the problem in your question.
You can change the mode from script.
Property Name: _Mode
With Debug.Log(MyRenderer.material.GetFloat("_Mode"));, I got the follwing values:
0 = Opaque
1 = Cutout
2 = Fade
3 = Transparent
We can change the Rendering Mode to Fade with MyRenderer.material.SetFloat("_Mode",2);
There is a known problem when setting the Render Mode from script. You must also update all other properties as well to make the change take effect. Here is a complete way to change your Render Mode to Fade from script:
MyRenderer.material.SetFloat("_Mode", 2);
MyRenderer.material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
MyRenderer.material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
MyRenderer.material.SetInt("_ZWrite", 0);
MyRenderer.material.DisableKeyword("_ALPHATEST_ON");
MyRenderer.material.EnableKeyword("_ALPHABLEND_ON");
MyRenderer.material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
MyRenderer.material.renderQueue = 3000;
Finally, if the this is working for you then your script is not good. You can use the script below to fade in and fade out a Mesh Renderer.
public MeshRenderer MyRenderer;
bool fading = false;
void Fade(bool fadeIn, float duration)
{
if (fading)
{
return;
}
fading = true;
changeModeToFade();
StartCoroutine(FadeTo(fadeIn, duration));
}
void changeModeToFade()
{
MyRenderer.material.SetFloat("_Mode", 2);
MyRenderer.material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
MyRenderer.material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
MyRenderer.material.SetInt("_ZWrite", 0);
MyRenderer.material.DisableKeyword("_ALPHATEST_ON");
MyRenderer.material.EnableKeyword("_ALPHABLEND_ON");
MyRenderer.material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
MyRenderer.material.renderQueue = 3000;
}
IEnumerator FadeTo(bool fadeIn, float duration)
{
//MyRenderer.material.
float counter = 0f;
//Set Values depending on if fadeIn or fadeOut
float a, b;
if (fadeIn)
{
a = 0;
b = 1;
}
else
{
a = 1;
b = 0;
}
//Enable MyRenderer component
if (!MyRenderer.enabled)
MyRenderer.enabled = true;
//Get original Mesh Color
Color meshColor = MyRenderer.material.color;
//Do the actual fading
while (counter < duration)
{
counter += Time.deltaTime;
float alpha = Mathf.Lerp(a, b, counter / duration);
Debug.Log(alpha);
MyRenderer.material.color = new Color(meshColor.r, meshColor.g, meshColor.b, alpha);
yield return null;
}
if (!fadeIn)
{
//Disable Mesh Renderer
MyRenderer.enabled = false;
}
fading = false; //So that we can call this function next time
}
void Start()
{
//Fade(true, 3f); //Fade In
Fade(false, 3f);//Fade Out
}
So heres my problem. I have a box that I want my character to move around. But I want to be able to move around it while holding multiple move commands, for instance..
when moving right (towards the left of the obstacle) I want to be able to hold move right and up or down at the same time without the character sticking to the box. The funny part is, it works fine for the left and right side of the obstacle, yet it sticks when i try it on the top and bottom side of the obstacle.
Heres the Player Class (object im moving)
<!-- language: c# -->
public class Player
{
public Texture2D texture;
public Vector2 position;
public int speed;
public Vector2 offset;
public bool left, right, up, down;
public Rectangle collisionRect
{
get
{
return new Rectangle((int)position.X , (int)position.Y, texture.Width, texture.Height);
}
}
public Vector2 direction;
public Player(Texture2D texture, Vector2 position, int speed)
{
this.texture = texture;
this.position = position;
this.speed = speed;
offset.X = speed;
offset.Y = speed;
left = false;
right = false;
up = false;
down = false;
}
public virtual void Update(GameTime gameTime, Rectangle clientBounds)
{
direction = Vector2.Zero;
if (Keyboard.GetState().IsKeyDown(Keys.A))
{
direction.X -= 1;
left = true;
}
else
left = false;
if (Keyboard.GetState().IsKeyDown(Keys.D))
{
direction.X += 1;
right = true;
}
else
right = false;
if (Keyboard.GetState().IsKeyDown(Keys.W))
{
direction.Y -= 1;
up = true;
}
else
up = false;
if (Keyboard.GetState().IsKeyDown(Keys.S))
{
direction.Y += 1;
down = true;
}
else
down = false;
position += (direction * speed);
}
public virtual void Draw(GameTime gameTime, SpriteBatch spritebatch)
{
spritebatch.Draw(texture, collisionRect, Color.White);
}
}
Heres the Update Method in my maingame
<!-- language: c# -->
public override void Update(GameTime gameTime)
{
// TODO: Add your update code here
player.Update(gameTime, Game.Window.ClientBounds);
if (player.right && HitWall(player))
{
player.position.X -= player.offset.X;
}
else if (player.left && HitWall(player))
{
player.position.X += player.offset.X;
}
if (player.down && HitWall(player))
{
player.position.Y -= player.offset.Y;
}
else if (player.up && HitWall(player))
{
player.position.Y += player.offset.Y;
}
base.Update(gameTime);
}
And the HitWall function
<!-- language: c# -->
public bool HitWall(Player player)
{
for (int i = player.collisionRect.Top; i < player.collisionRect.Bottom; i++)
for (int j = player.collisionRect.Left; j < player.collisionRect.Right; j++)
if (TextureData[i * gameMap.map.Width + j] != Color.White)
return true;
return false;
}
I'm not sure where offset is defined, but I'm assuming it's the movement you've just made that frame.
Your problem is that because you check left & right before up and down, if you're moving diagonally down onto the top edge of the box, then you'll register a hit in the Y direction — HitWall doesn't check which direction you're going, it just checks for a collision. Therefore, the collision in the Y axis stil counts on the line if (player.right && HitWall(player)) and stops your lateral movement.
Best bet is to apply your sideways movement, check for a hit, move back if there is one — then apply your downwards movement, check for a hit, and move back if there is one. Correcting the position like this should mean you slide along the sides as you want.