I want to change between 2 materials, depending on the platforms (gameobject) rotation.
Here is what I've done so far:
public class PlatformSpawner : MonoBehaviour
{
public GameObject platform;
public Material[] platformMaterial;
Material currentMaterial;
Renderer _renderer;
}
void Start()
{
_renderer = this.GetComponent<Renderer>();
}
I also wrote this, but I don't want to change materials by buttons:
public void LeftTurn()
{
_renderer.material = platformMaterial[0];
currentMaterial = _renderer.material;
}
public void RightTurn()
{
_renderer.material = platformMaterial[1];
currentMaterial = _renderer.material;
}
}
And this is where the platform rotates randomly 90 degrees to the left or to the right:
public struct SpawnPoint
{
public Vector3 position;
public Quaternion orientation;
public void Step(float distance)
{
if (Random.value < 0.5)
{
position.x += distance;
orientation = Quaternion.Euler(0, 90, 0); //change to one of the materials
}
else
{
position.z += distance;
orientation = Quaternion.Euler(0, 0, 0); //change to the other of the materials.
//This is where I want to material to switch.
//When the objects position changes, the material changes too.
}
}
}
There is a picture of the gameplay. I want to change the material of all the corner platforms to have a nice curve line view.
Can anyone help me what and how to do in this case? I am a bit lost there.
Every help is highly appreciated!
EDIT: new code looks like that. The only issue is that Unity gives me 15 errors (see on the picture below), even if Visual Studio says no issue has been found. The errors refer to the switch.
public class PlatformSpawner : MonoBehaviour
{
public GameObject platform;
public Transform lastPlatform;
SpawnPoint _spawn;
bool stop;
public Material straightMaterial;
public Material turnLeftMaterial;
public Material turnRightMaterial;
public Renderer roadPrefab;
[System.Serializable]
public struct SpawnPoint
{
public Vector3 position;
public Quaternion orientation;
public RoadType type;
public enum RoadType
{
Straight,
LeftTurn,
RightTurn
}
private enum Direction
{
Z,
X,
}
private Direction lastDirection;
public void Step(float distance)
{
type = RoadType.Straight;
if (Random.value < 0.5f)
{
position.x += distance;
orientation = Quaternion.Euler(0, 90, 0);
if (lastDirection == Direction.Z)
{
type = RoadType.RightTurn;
}
}
else
{
position.z += distance;
orientation = Quaternion.Euler(0, 0, 0);
if (lastDirection == Direction.X)
{
type = RoadType.LeftTurn;
}
lastDirection = Direction.Z;
}
}
}
void Start()
{
_spawn.position = lastPlatform.position;
_spawn.orientation = transform.rotation;
StartCoroutine(SpawnPlatforms());
}
IEnumerator SpawnPlatforms()
{
while (!stop)
{
var _spawn = new SpawnPoint();
for (var i = 0; i < 20; i++)
{
var newPlatform = Instantiate(roadPrefab, _spawn.position, _spawn.orientation);
_spawn.Step(1.5f);
var roadMaterial = _spawn.type switch
{
SpawnPoint.RoadType.LeftTurn => turnLeftMaterial,
SpawnPoint.RoadType.RightTurn => turnRightMaterial,
_ => straightMaterial
};
newPlatform.GetComponent<Renderer>().material = roadMaterial;
yield return new WaitForSeconds(0.1f);
}
}
}
}
So it sounds like you basically have a working system for switching the materials and spawning you road parts and materials already look correctly according to your rotations - now you only need to identify the curves.
Actually this is pretty simple:
is the current part in X direction and the next will be in Z -> Left Turn
is the current part in Z direction and the next will be in X -> RightTurn
any other case is straight
So you could probably do something like
public struct SpawnPoint
{
public Vector3 position;
public Quaternion orientation;
public RoadType type;
public enum RoadType
{
Straight,
LeftTurn,
RightTurn
}
private enum Direction
{
// since your orientation by default equals the Z direction use that as default value for the first tile
Z,
X,
}
private Direction lastDirection;
public void Step(float distance)
{
type = RoadType.Straight;
if (Random.value < 0.5f)
{
position.x += distance;
orientation = Quaternion.Euler(0, 90, 0);
if(lastDirection == Direction.Z)
{
type = RoadType.RightTurn;
}
lastDirection = Direction.X;
}
else
{
position.z += distance;
orientation = Quaternion.Euler(0, 0, 0);
if(lastDirection == Direction.X)
{
type = RoadType.LeftTurn;
}
lastDirection = Direction.Z;
}
}
}
And you didn't show your spawn code but I would assume something like
public class Example : MonoBehaviour
{
public Material straightMaterial;
public Material turnLeftMaterial;
public Material turnRightMaterial;
public Renderer roadPrefab;
private void Awake()
{
var spawnPoint = new SpawnPoint();
for(var i = 0; i < 20; i++)
{
var roadTile = Instantiate(roadPrefab, spawnPoint.position, spawnPoint.orientation);
// do the Step after spawning the current tile but before assigning the material
// -> we want/need to know already where the next tile is going to be
spawnPoint.Step(1f);
var roadMaterial = spawnPoint.type switch
{
SpawnPoint.RoadType.LeftTurn => turnLeftMaterial,
SpawnPoint.RoadType.RightTurn => turnRightMaterial,
_ => straightMaterial
};
roadTile.GetComponent<Renderer>().material = roadMaterial;
}
}
}
Behold my Paint skills ;)
This will get you started using Quaternion.Dot.
using UnityEngine;
[RequireComponent(typeof(Renderer))]
public class NewBehaviourScript : MonoBehaviour
{
public Material Material1;
public Material Material2;
public Vector3 Euler = new(90, 0, 0);
private Renderer _renderer;
private void Start()
{
_renderer = GetComponent<Renderer>();
_renderer.material = Material1;
}
private void Update()
{
var dot = Quaternion.Dot(transform.rotation, Quaternion.Euler(Euler));
if (Mathf.Approximately(dot, 1.0f))
{
_renderer.material = Material2;
}
else
{
_renderer.material = Material1;
}
}
}
Using different materials for N, E, S, W corners:
using UnityEngine;
[RequireComponent(typeof(Renderer))]
public class NewBehaviourScript : MonoBehaviour
{
public Material Material1;
public Material Material2;
public Material Material3;
public Material Material4;
public Vector3 Euler1 = new(0, 0, 0);
public Vector3 Euler2 = new(0, 90, 0);
public Vector3 Euler3 = new(0, 180, 0);
public Vector3 Euler4 = new(0, 270, 0);
private Renderer _renderer;
private void Start()
{
_renderer = GetComponent<Renderer>();
_renderer.material = Material1;
}
private void Update()
{
if (Mathf.Approximately(Quaternion.Dot(transform.rotation, Quaternion.Euler(Euler1)), 1.0f))
{
_renderer.material = Material1;
}
if (Mathf.Approximately(Quaternion.Dot(transform.rotation, Quaternion.Euler(Euler2)), 1.0f))
{
_renderer.material = Material2;
}
if (Mathf.Approximately(Quaternion.Dot(transform.rotation, Quaternion.Euler(Euler3)), 1.0f))
{
_renderer.material = Material3;
}
if (Mathf.Approximately(Quaternion.Dot(transform.rotation, Quaternion.Euler(Euler4)), 1.0f))
{
_renderer.material = Material4;
}
}
}
Make sure to wrap the rotation past 360 degrees, else it'll always look yellow (4th material).
Related
I'm making this flappy bird replica but instead of a bird it's a rocket.
The controlls are different however the idea is the same.
I have setup a GameManager script:
using UnityEngine;
public class GameManager : MonoBehaviour
{
// Script Refrences
public BarrierSpawnner spawnner;
public MainMen menu;
public MoveLeft moveLeft;
public rockeyMovement movement;
void Start()
{
spawnner.StartedGame = false;
movement.dead = false;
spawnner.StartedGame = false;
movement.rend.enabled = true;
}
void Update()
{
if (spawnner.StartedGame)
{
menu.menuOn = false;
}
if (movement.dead == true)
{
spawnner.StartedGame = false;
}
if (spawnner.StartedGame == false)
{
menu.menuOn = true;
}
}
}
And a Movement Script:
using UnityEngine;
public class rockeyMovement : MonoBehaviour
{
// Variables
private float gravity;
private Vector2 startPos;
public bool dead;
// Refrences
public Rigidbody2D rb;
public BarrierSpawnner spawnner;
public Renderer rend;
public MainMen menStart;
public static rockeyMovement Instance { get; private set; }
void Start()
{
Instance = this;
startPos = transform.position;
rb = GetComponent<Rigidbody2D>();
gravity = rb.gravityScale;
rb.gravityScale = 0;
rend = GetComponent<Renderer>();
}
void OnCollisionStay2D(Collision2D collision)
{
dead = true;
gravity = rb.gravityScale;
rb.gravityScale = 0;
startPos = transform.position;
}
void Update()
{
if (spawnner.StartedGame)
{
rb.gravityScale = 2;
}
if (spawnner.StartedGame == false)
{
transform.position = new Vector2(0, 0);
}
Vector2 vel = rb.velocity;
float ang = Mathf.Atan2(vel.y, 10) * Mathf.Rad2Deg;
transform.rotation = Quaternion.Euler(new Vector3(0, 0, ang - 90f));
if (Input.GetButton("Jump"))
{
rb.AddForce(Vector2.up * gravity * Time.deltaTime * 1000f);
}
}
}
As well as a MenuScript:
using UnityEngine;
public class MainMen : MonoBehaviour
{
public BarrierSpawnner spawnner;
public GameObject CanvasObject;
public bool menuOn;
void Update()
{
if (menuOn)
{
CanvasObject.GetComponent<Canvas> ().enabled = true;
}
}
public void Go()
{
spawnner.StartedGame = true;
CanvasObject.GetComponent<Canvas> ().enabled = false;
}
}
The game starts out normally in the correct way, the problems pop up the moment the player dies. When the player comes in contact with the obstacles I have the script set a bool to true "dead". And when "dead" is true the menu pops back again however, I you cannot interact with it and the page stays like that.
How do I fix this issue?
Is my logic right?
If not, where did I go wrong?
Don't disable Canvas component. Instead, create a panel with buttons on it and activate this panel when needed. Keep it deactivated otherwise.
Hello I want to Make the gun of my turret face the direction my camera is facing at.
I don't really know Complex Maths so can you keep it simple. Thanks
My code is this:
gun.localEulerAngles = new Vector3(tankCamera.eulerAngles.x - transform.eulerAngles.x, 0);
Here is the Complete Class:
public class TurretController : MonoBehaviour
{
public Transform turret;
public Transform gun;
public Transform tankCamera;
[Header("Values")]
public float lowestX = 23;
public float highestX = -9;
public float turnSpeed = 10f;
public float additionalX = 90f;
public float maxRadians = 90f;
public bool gunLocal = true;
public bool turretRotation = false;
public LayerMask layerMaskOfGun;
[Header("Read-Only")] [SerializeField] private Vector3 angle;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (turretRotation)
{
turret.localRotation = Quaternion.RotateTowards(turret.localRotation,
new Quaternion(0, tankCamera.rotation.y, 0, turret.localRotation.w),
Time.deltaTime * turnSpeed);
}
else
{
angle = new Vector3(0, (tankCamera.eulerAngles.y - transform.eulerAngles.y));
//turret.localEulerAngles = Vector3.Lerp(turret.localEulerAngles, angle, Time.deltaTime / turnSpeed);
turret.localEulerAngles = angle;
}
//if (gun.localRotation.x <= lowestX || gun.localRotation.x >= highestX)
{
if (gunLocal)
{
gun.localEulerAngles = new Vector3(tankCamera.eulerAngles.x - transform.eulerAngles.x, 0);
//gun.localRotation = new Quaternion(tankCamera.rotation.x, 0f, 0f, gun.rotation.w);
}
else
{
Camera camera;
//RaycastHit hit;
//if (Physics.Raycast(tankCamera.position, tankCamera.forward, out hit, -layerMaskOfGun))
{
//gun.LookAt(hit.point);
gun.LookAt(tankCamera.forward);
}
}
}
}
}
using UnityEngine;
public class Movement : MonoBehaviour
{
public Rigidbody rb;
public float f = 100;
public float b = -100;
public float l = -100;
public float r = 100;
public float u = 10;
public bool onGround = true;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (onGround)
{
if (Input.GetKeyDown("space"))
{
rb.AddForce(0, u, 0, ForceMode.VelocityChange);
onGround = false;
}
}
if (Input.GetKey("w"))
{
rb.AddForce(0, 0, f);
}
if (Input.GetKey("a"))
{
rb.AddForce(l, 0, 0);
}
if (Input.GetKey("d"))
{
rb.AddForce(r, 0, 0);
}
if (Input.GetKey("s"))
{
rb.AddForce(0, 0, b);
}
if(onGround == false)
{
rb.AddForce(Physics.gravity * rb.mass);
}
}
private void OnCollisionEnter(Collision collision)
{
if(collision.collider.tag == "Ground")
{
onGround = true;
}
}
}
This is my code. Since I wanted my cube to topple while moving, I would like to add forces above center of mass with a slight delay. According to how I visualize, this will make the cube topple.
How could I do this, Is this a good way of doing this or there is another way to topple my cube? Thanks.
you can do it in unity directly by adding physical material to the cube in it's (Box Collider) and changing the values, but if you want to do it from code you have to mention it first and then edit its values
like this:
using UnityEngine;
public class TestPlayerMovement : MonoBehaviour {
public BoxCollider BX;
// and then drag it from unity
void FixedUpdate () {
//it's better to use FixedUpdate bec it's movement
//examples
BX.material.bounciness = 0;
BX.material.dynamicFriction = 0;
BX.material.staticFriction = 0;
print (BX.material.staticFriction);
print (BX.material.bounciness);
print (BX.material.dynamicFriction);
}
I'm tired...
I'm tried to make a simple 2d planetary shooter and started from creating my own physic with Newton's law of universal gravitation(something like that), but my code isn't working - bodies moves from each other, while they Colliders is colliding, and I don't know why.(even inertia)
So, I've tried to capy and paste code from another StackOverflow, but its not working too. Requesting some help!
https://imgur.com/qvnLxSk
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(CircleCollider2D), typeof(CircleCollider2D))]
public class MassEffect : MonoBehaviour
{
//public GameObject player;
private float gRadius = 5f;
public float rotSpeed = 0.5f;
public float gravitation = 50f;
private HashSet<Rigidbody2D> affectedBodies = new HashSet<Rigidbody2D>();
private Rigidbody2D componentRigidbody;
private CircleCollider2D gTrigger;
private void Start()
{
componentRigidbody = GetComponent<Rigidbody2D>();
gTrigger = GetComponents<CircleCollider2D>()[1];
gTrigger.isTrigger = true;
gTrigger.radius = gRadius / transform.localScale.x;
}
private void OnTriggerEnter2D(Collider2D other)
{
var rbInRadius = other.GetComponent<Rigidbody2D>();
if (rbInRadius != null)
{
affectedBodies.Add(rbInRadius);
}
}
private void OnTriggerExit2D(Collider2D other)
{
var rbInRadius = other.GetComponent<Rigidbody2D>();
if (rbInRadius != null)
{
affectedBodies.Remove(rbInRadius);
}
}
private void FixedUpdate()
{
transform.Rotate(Vector3.forward * rotSpeed);
foreach (var objectInVicinity in affectedBodies)
{
if (objectInVicinity == null)
{
affectedBodies.Remove(objectInVicinity);
break;
}
float dist = Vector2.Distance(transform.position, objectInVicinity.transform.position);
float gravitationFactor = 1 - dist / gRadius;
Vector2 force = (transform.position - objectInVicinity.transform.position).normalized * gravitation * gravitationFactor;
objectInVicinity.AddForce(force);
}
}
void OnDrawGizmos()
{
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(transform.position, gRadius);
}
}
I fight with that for 2 days and searching internet but i don't have any answer for my problem. In few words i want to have orbiting camera on screen and objects which I want also rotate independently from my camera. I implement Arcball rotation controller and I use it to rotate all object in the scene (maybe this cause problem). It looks ok when i have camera in starting point but when i start moving it and I want to rotate objects on the scene its looks like all object rotate like im in the camera starting point. Maybe here someone can help :D
My Arcball class:
public class ArcBallGL{
private bool isBeingDragged;
private Vector3 downPoint;
private Vector3 currentPoint;
public Vector3 BallCenter { get; set; }
private Quaternion down;
public Quaternion Now { get; set; }
private float width, height;
public ArcBallGL()
{
Reset();
width = height = 0.0f;
}
public void Reset()
{
isBeingDragged = false;
downPoint = Vector3.Zero;
currentPoint = Vector3.Zero;
down = Quaternion.Identity;
Now = Quaternion.Identity;
}
public void SetWindow(float width, float height)
{
this.width = width;
this.height = height;
}
public void OnBeginDrag(float x, float y)
{
isBeingDragged = true;
down = Now;
downPoint = ScreenToVector(x, y);
}
public void OnMove(float x, float y)
{
if (isBeingDragged == true)
{
currentPoint = ScreenToVector(x, y);
Now = down * QuatFromBallPoints(downPoint, currentPoint);
}
}
public void OnStopDrag()
{
isBeingDragged = false;
}
public Matrix4 GetRotationMatrix()
{
return Matrix4.CreateFromQuaternion(Now);
}
public Matrix4 GetRotationMatrixFormViewPort(Matrix4 camera)
{
var viewMatrix = Matrix4.CreateFromQuaternion(Now).Inverted();
viewMatrix.Normalize();
return viewMatrix * Matrix4.CreateFromQuaternion( camera.ExtractRotation()).Inverted();
}
private Vector3 ScreenToVector(float screentPointX, float screenPointY)
{
float x = (screentPointX - width * 0.5f) / (width * 0.5f);
float y = (screenPointY - height * 0.5f) / height;
float z = 0.0f;
float mag = x * x + y * y;
if (mag > 1.0f)
{
float scale = 1.0f / (float)Math.Sqrt(mag);
x *= scale;
y *= scale;
}
else
{
z = (float)Math.Sqrt(1.0f - mag);
}
return new Vector3(-x, y, -z);
}
private Quaternion QuatFromBallPoints(Vector3 from, Vector3 to)
{
float dot = Vector3.Dot(from, to);
Vector3 part = Vector3.Cross(from, to);
return new Quaternion(part.X, part.Y, part.Z, dot);
}
}
My Camera class:
public class Camera{
public Vector3 Position = Vector3.Zero;
private Vector3 Up = Vector3.UnitY;
public float MoveSpeed = 0.2f;
public float RotationSpeed = 0.01f;
public Quaternion RotQuar { get; set; }
public Camera(Vector3 startPos = new Vector3())
{
Position = startPos;
RotQuar = Quaternion.Identity;
}
public Matrix4 GetViewMatrix(Vector3 target)
{
//RotQuar.Conjugate();
var rotMatrix = Matrix4.CreateFromQuaternion(RotQuar);
var camPosition = Vector3.Transform(Position, rotMatrix);
var camDir = target - camPosition;
camDir.Normalize();
var camRight = Vector3.Cross(camDir, Vector3.UnitY);
camRight.Normalize();
var camUp = Vector3.Cross(camRight, camDir);
camUp.Normalize();
var cameraMat = Matrix4.LookAt(camPosition, target , camUp) ;
return cameraMat;
}
}
My mouse move methood
private void MouseMoved(object sender, MouseMoveEventArgs e)
{
if (e.Mouse.IsButtonDown(MouseButton.Left) && RIsDown)
{
foreach (Shape s in _shapes)
{
if (s.IsSelected)
{
Rotation.OnMove(e.Position.X, e.Position.Y);
s.RoatationQuat = Rotation.Now;
s.RotationMatrix = Rotation.GetRotationMatrixFormViewPort(_camera.GetViewMatrix(Vector3.Zero));
}
}
}
if (e.Mouse.IsButtonDown(MouseButton.Left) && CIsDown)
{
Rotation.OnMove(e.Position.X, e.Position.Y);
_camera.RotQuar = Rotation.Now;
}
}
I use index buffer to draw my objects on the scene. I think the problem lies in calculating the camera coords to objects coords but i don't know where i must calculate it. Thanks for help