Linear Primitives
Raycasting
Raycasting is a method used in computer graphics to render 3D scenes by tracing light rays from a viewpoint into the scene. It’s essentially the opposite of ray tracing, which traces rays from light sources to the viewer. In simpler terms, raycasting simulates how light will hit the eye or camera in a virtual environment.
Raycasting in Unity
Raycasting is a technique for detecting collisions in 3D space by simulating a “laser beam” from a point in space. It’s used to determine what objects, if any, a line segment intersects with. This information can be used for various game mechanics, like shooting, selecting objects, or checking for obstacles.
- Pre-setting before scripts
- Step1: Create three Cube GameObeject called Wall1-Wall3. One can change the sizes of the walls to make it reasonable.
- Wall1: Position(1.5,0,-1.5), Scale(3,1,1)
- Wall2: Position(0,0,1), Scale(1,1,3), set layer in the inspector to World
- Wall3: Position(1.5,0,3), Scale(2,1,1)
- Step2: Create another Cube GameObject “RayOrigin” and attach the script Raycasting
- RayOrigin: Position(5,0,0)
- Step3: Create another Cube GameObject “RayReflection” and attach the script RaycastingReflection
- RayOrigin: Position(5,0,0)
- Step4: Reflection set to 3 and MaxLength set to 100
- Step5: Add “Mirror” tag to the Wall 1-3
- Step6: Enable LineRenderer.CornerVertices to make the line looks smoother. i.e., 10.
- Step7: Enable LineRenderer.EndCapVertices to make the line looks smoother. i.e., 10.
- Step8: Change the width or add material…
- Step1: Create three Cube GameObeject called Wall1-Wall3. One can change the sizes of the walls to make it reasonable.
Unity Layer
Layers are a tool that allows you to separate GameObjects in your scenes. You can use layers through the UI and with scripts to edit how GameObjects within your scene interact with each other.
Raycasting.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Raycasting : MonoBehaviour
{
private Ray _ray;
// Container for hit data
private RaycastHit _hitData;
public LayerMask layers;
// Initialization, usually instantiation of objects
// and setting up references to other objects.
private void Awake()
{
}
// Start is called before the first frame update
void Start()
{
// Creates a Ray from this object, moving forward
// Vector3.left for the X-Axis, Vector3.up for the Y-Axis and Vector3.forward for the Z-Axis.
// _ray = new Ray(transform.position, transform.forward);
// Creates a Ray from the center of the viewport
// _ray = Camera.main.ViewportPointToRay(new Vector3 (0.5f, 0.5f, 0));
// Creates a Ray from the mouse position
// _ray = Camera.main.ScreenPointToRay(Input.mousePosition);
layers = LayerMask.GetMask("Default");
// layers = LayerMask.GetMask("World") | LayerMask.GetMask("Water");
// layers = 1<<9;
// Debug.Log("test!");
}
// Update is called once per frame
void Update()
{
FireRay();
}
void FireRay()
{
// Set ray origin and direction
_ray = new Ray(transform.position, transform.forward);
// Visualize the ray in debug
Debug.DrawRay(_ray.origin, _ray.direction * 10);
// One of the most common ways to use Raycast is using the Physics Class, which returns a boolean true or false, depending on if the Ray hit anything.
// The out keyword in C# is used to return extra information from a function.
// ref requires the variable to be initialized before being passed to the method, whereas out does not, and the method is responsible for initializing it before returning.
if (Physics.Raycast(_ray, out _hitData, 10, layers))
{
Vector3 hitPosition = _hitData.point;
float hitDistance = _hitData.distance;
// Reads the Collider name
string name = _hitData.collider.name;
// Gets a Game Object reference from its Transform
GameObject hitObject = _hitData.transform.gameObject;
// Debug.Log("hitted object name = " + name);
Debug.Log("hitted object name = " + hitObject.name);
}
}
}
Raycasting Reflection
RaycastReflection.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(LineRenderer))]
public class RaycastReflection : MonoBehaviour
{
[SerializeField]
private int _reflections;
[SerializeField]
private float _maxLength;
private LineRenderer _lineRenderer;
private Ray _ray;
private RaycastHit _hitData;
// private Vector3 _direction;
void Awake()
{
_lineRenderer = GetComponent<LineRenderer>();
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
// Set ray origin and direction
_ray = new Ray(transform.position, transform.forward);
// initialization of the line renderer
// positionCount returns the number of vertices in the line.
_lineRenderer.positionCount = 1;
// Set the position of a vertex in the line.
_lineRenderer.SetPosition(0, transform.position);
float remainingLength = _maxLength;
for (int i = 0; i < _reflections; i++)
{
// retrun true if the ray hits something
if(Physics.Raycast(_ray, out _hitData, remainingLength))
{
_lineRenderer.positionCount += 1;
_lineRenderer.SetPosition(_lineRenderer.positionCount-1, _hitData.point);
remainingLength -= Vector3.Distance(_ray.origin, _hitData.point);
if (_hitData.collider.tag == "Mirror"){
// _hitData.normal stores the normal of the surface the ray hit.
_ray = new Ray(_hitData.point, Vector3.Reflect(_ray.direction, _hitData.normal));
}
}
else
{
// Calculate the remaining length of the ray
_lineRenderer.positionCount += 1;
_lineRenderer.SetPosition(_lineRenderer.positionCount - 1, _ray.origin + _ray.direction * remainingLength);
}
}
// Debug.Log("positionCount: " + _lineRenderer.positionCount);
}
}
Curve
Bézier Curve in Unity
- Pre-setting before scripts
- Step1: Create an empty game object, name Curve.
- Step2: Create 4 empty game objects, name ControlPoint1-4 and select icon for them (under inspector text).
- Step3: Drag the curve script to the Curve game object and set control points to 4
- Step4: Drag control points to the corresponding control point elements
Curve2D.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// Pre-setting before scripts
// Step1: Create an empty game object, name Curve.
// Step2: Create 4 empty game objects, name ControlPoint1-4 and select icon for them (under inspector text).
// Step3: Drag the curve script to the Curve game object and set control points to 4
// Step4: Drag control points to the corresponding control point elements
public class Curve2D : MonoBehaviour
{
[SerializeField]
private Transform[] controlPoints;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
private void OnDrawGizmos()
{
// Draw the interpolated points using spheres
for(float t = 0; t <= 1; t += 0.05f)
{
Vector2 gizmosPosition = Mathf.Pow(1 - t, 3) * controlPoints[0].position + 3 * Mathf.Pow(1 - t, 2) * t * controlPoints[1].position + 3 * (1 - t) * Mathf.Pow(t, 2) * controlPoints[2].position + Mathf.Pow(t, 3) * controlPoints[3].position;
Gizmos.DrawSphere(gizmosPosition, 0.25f);
}
// Draw the lines between the control points
Gizmos.DrawLine(controlPoints[0].position, controlPoints[1].position);
Gizmos.DrawLine(controlPoints[2].position, controlPoints[3].position);
// Gizmos.DrawLine(new Vector2(controlPoints[0].position.x, controlPoints[0].position.y), new Vector2(controlPoints[1].position.x, controlPoints[1].position.y));
// Gizmos.DrawLine(new Vector2(controlPoints[2].position.x, controlPoints[2].position.y), new Vector2(controlPoints[3].position.x, controlPoints[3].position.y));
}
}
Curve3D.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Curve3D : MonoBehaviour
{
[SerializeField]
private Transform[] controlPoints;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
private void OnDrawGizmos()
{
// Draw the interpolated points using spheres
for (float t = 0; t <= 1; t += 0.05f)
{
Vector3 gizmosPosition = Mathf.Pow(1 - t, 3) * controlPoints[0].position + 3 * Mathf.Pow(1 - t, 2) * t * controlPoints[1].position + 3 * (1 - t) * Mathf.Pow(t, 2) * controlPoints[2].position + Mathf.Pow(t, 3) * controlPoints[3].position;
Gizmos.DrawSphere(gizmosPosition, 0.25f);
}
// Draw the lines between the control points
Gizmos.DrawLine(controlPoints[0].position, controlPoints[1].position);
Gizmos.DrawLine(controlPoints[2].position, controlPoints[3].position);
// Gizmos.DrawLine(new Vector3(controlPoints[0].position.x, controlPoints[0].position.y, controlPoints[0].position.z), new Vector3(controlPoints[1].position.x, controlPoints[1].position.y, controlPoints[1].position.z));
// Gizmos.DrawLine(new Vector3(controlPoints[2].position.x, controlPoints[2].position.y, controlPoints[2].position.z), new Vector3(controlPoints[3].position.x, controlPoints[3].position.y, controlPoints[3].position.z));
}
}
- Pre-setting before scripts
- Step1: Create a cube game object called Follower and attach the script to it.
- Step2: Change the Route size in the inspector to 1 and drag the Curve object there.
- Step3: Create the 2nd Curve object and change the Route size to 2 in the inspector.
CurveFollow.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CurveFollow : MonoBehaviour
{
[SerializeField]
Transform[] routes;
private int _routeToFollow;
private float _tParam;
private float _speedModifier;
private bool _coroutineAllowed;
// Start is called before the first frame update
void Start()
{
_routeToFollow = 0;
_tParam = 0f;
_speedModifier = 0.3f;
_coroutineAllowed = true;
}
// Update is called once per frame
void Update()
{
if (_coroutineAllowed)
{
StartCoroutine(FollowTheRoute(_routeToFollow));
}
}
// This function will be executed across the frames
private IEnumerator FollowTheRoute(int routeNum)
{
_coroutineAllowed = false;
Vector3 p0 = routes[routeNum].GetChild(0).position;
Vector3 p1 = routes[routeNum].GetChild(1).position;
Vector3 p2 = routes[routeNum].GetChild(2).position;
Vector3 p3 = routes[routeNum].GetChild(3).position;
while (_tParam < 1)
{
_tParam += Time.deltaTime * _speedModifier;
transform.position = Mathf.Pow(1 - _tParam, 3) * p0 + 3 * Mathf.Pow(1 - _tParam, 2) * _tParam * p1 + 3 * (1 - _tParam) * Mathf.Pow(_tParam, 2) * p2 + Mathf.Pow(_tParam, 3) * p3;
// Can be used to wait until the end of the frame to have a screenshot
// yield return new WaitForEndOfFrame();
// yield return new WaitForSeconds(0.03f);
// Suspend the coroutine until the next frame
yield return null;
}
// Reset the tParam to 0 and move to the next route
_tParam = 0.0f;
_routeToFollow += 1;
_routeToFollow %= routes.Length;
_coroutineAllowed = true;
}
}