C# to
TypeScript
Translate your Unity C# knowledge to TypeScript for web development with Needle Engine.
New to TypeScript?
Start with TypeScript Essentials for core language concepts without C# comparisons.
Value Types vs Reference Types
C# - Value Types (Structs)
In C#, Vector3 is a struct (value type)—it gets copied when passed to methods:
void MyCallerMethod() {
var position = new Vector3(0, 0, 0);
MyExampleVectorMethod(position);
Debug.Log("Position.x is " + position.x); // ✅ Still 0 (copy was modified)
}
void MyExampleVectorMethod(Vector3 position) {
position.x = 42; // Modifies the COPY, not the original
}Key behavior: Assignment creates a copy:
var myVector = new Vector3(1, 1, 1);
var myOtherVector = myVector; // Creates a COPY
myOtherVector.x = 42;
// Logs: 1, 42 (two separate instances)
Debug.Log(myVector.x + ", " + myOtherVector.x);
TypeScript - Reference Types (Objects)
In JavaScript/TypeScript, all objects are reference types—including Vector3:
import { Vector3 } from 'three'
function myCallerMethod() {
const position = new Vector3(0, 0, 0);
myExampleVectorMethod(position);
console.log("Position.x is " + position.x); // ⚠️ Now 42 (original modified!)
}
function myExampleVectorMethod(position: Vector3) {
position.x = 42; // Modifies the ORIGINAL
}Key behavior: Assignment creates a reference:
import { Vector3 } from 'three'
const myVector = new Vector3(1, 1, 1);
const myOtherVector = myVector; // Creates a REFERENCE
myOtherVector.x = 42;
// Logs: 42, 42 (same instance!)
console.log(myVector.x, myOtherVector.x);Critical Difference!
In TypeScript, modifying a Vector modifies the original. To create a copy, use .clone():
const myVector = new Vector3(1, 1, 1);
const myOtherVector = myVector.clone(); // ✅ Creates a true copy
myOtherVector.x = 42;
// Logs: 1, 42 (two separate instances)
console.log(myVector.x, myOtherVector.x);Vector Math & Operators
C# - Operator Overloading
C# supports operator overloading for vectors:
var myVector = new Vector3(1, 1, 1);
var myFactor = 100f;
myVector *= myFactor; // ✅ Operators work
myVector = myVector + new Vector3(5, 0, 0); // ✅ Addition works
// → myVector is now (105, 100, 100)
TypeScript - Method Calls
JavaScript/TypeScript does not support operator overloading. Use methods instead:
import { Vector3 } from "three"
const myVector = new Vector3(1, 1, 1);
const myFactor = 100;
myVector.multiplyScalar(myFactor); // Can't use *= operator
myVector.add(new Vector3(5, 0, 0)); // Can't use + operator
// → myVector is now (105, 100, 100)Common Vector Operations
| Operation | Unity (C#) | Needle Engine (TypeScript) |
|---|---|---|
| Multiply | vector *= 2 | vector.multiplyScalar(2) |
| Add | vector += other | vector.add(other) |
| Subtract | vector -= other | vector.sub(other) |
| Length | vector.magnitude | vector.length() |
| Normalize | vector.normalized | vector.normalize() |
| Distance | Vector3.Distance(a, b) | a.distanceTo(b) |
three.js Vector Methods
Most three.js vector methods modify the original vector. For immutable operations, clone first:
const result = myVector.clone().multiplyScalar(2); // Original unchangedTime & Delta Time
Unity Time
void Update() {
transform.position.x += speed * Time.deltaTime;
float currentTime = Time.time;
}
Needle Engine Time
Access time data via this.context.time:
update() {
this.gameObject.position.x += this.speed * this.context.time.deltaTime;
const currentTime = this.context.time.time;
}Property Mapping
| Unity (C#) | Needle Engine (TypeScript) | Description |
|---|---|---|
Time.time | this.context.time.time | Scaled time since app started |
Time.deltaTime | this.context.time.deltaTime | Time since last frame |
Time.frameCount | this.context.time.frameCount | Total frames rendered |
Time.realtimeSinceStartup | this.context.time.realtimeSinceStartup | Unscaled time |
Time.timeScale | this.context.time.timeScale | Time multiplier |
Frame-Rate Independent Movement
Always multiply movement by deltaTime:
this.gameObject.position.x += speed * this.context.time.deltaTime;Raycasting
Unity Physics.Raycast
if (Physics.Raycast(origin, direction, out RaycastHit hit, maxDistance)) {
Debug.Log("Hit: " + hit.collider.name);
}
Needle Engine Raycast
// Screen-space raycast from mouse/touch
const hits = this.context.physics.raycast();
if (hits.length > 0) {
console.log("Hit:", hits[0].object.name);
}
// Custom ray with options
const hits = this.context.physics.raycast({
maxDistance: 100,
lineOfSight: true
});
// Physics-based (requires colliders)
const hit = this.context.physics.engine?.raycast();Key differences:
- Unity: Requires colliders for all raycasts
- Needle Engine: Default raycasts hit visible geometry, optional physics-based raycasts require colliders
Input
Unity Input
void Update() {
if (Input.GetMouseButtonDown(0)) {
Debug.Log("Click!");
}
if (Input.GetKey(KeyCode.Space)) {
Debug.Log("Space pressed");
}
}
Needle Engine Input
Polling input state:
update() {
if (this.context.input.getPointerDown(0)) {
console.log("Click!");
}
if (this.context.input.getKeyDown("Space")) {
console.log("Space pressed");
}
}Event subscription:
import { InputEvents, NEPointerEvent } from "@needle-tools/engine";
onEnable() {
this.context.input.addEventListener(InputEvents.PointerDown, this.onPointerDown);
}
onDisable() {
this.context.input.removeEventListener(InputEvents.PointerDown, this.onPointerDown);
}
private onPointerDown = (evt: NEPointerEvent) => {
console.log("Pointer down at:", evt.point);
}InputSystem Callbacks
Unity IPointerClickHandler
using UnityEngine.EventSystems;
public class Clickable : MonoBehaviour, IPointerClickHandler {
public void OnPointerClick(PointerEventData eventData) {
Debug.Log("Clicked!");
}
}
Needle Engine Pointer Events
import { Behaviour, PointerEventData } from "@needle-tools/engine";
export class Clickable extends Behaviour {
onPointerClick(args: PointerEventData) {
console.log("Clicked at:", args.point);
}
}Available events:
onPointerDownonPointerUponPointerEnteronPointerMoveonPointerExitonPointerClick
All receive a PointerEventData argument.
Debug.Log
Unity Debug
Debug.Log("Hello Unity");
Debug.LogWarning("Warning message");
Debug.LogError("Error message");
JavaScript Console
console.log("Hello web");
console.warn("Warning message");
console.error("Error message");
// Log multiple values
console.log("Score:", score, "Player:", player);Debug Gizmos
Unity Gizmos
void OnDrawGizmos() {
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(transform.position, 1.0f);
Gizmos.DrawLine(start, end);
}
Needle Engine Gizmos
import { Gizmos, isDevEnvironment } from "@needle-tools/engine";
update() {
if (isDevEnvironment()) {
Gizmos.DrawWireSphere(this.gameObject.position, 1.0, 0xff0000, 1);
Gizmos.DrawLine(start, end, 0x00ff00, 0);
}
}Key differences:
| Unity | Needle Engine |
|---|---|
OnDrawGizmos() method | Call Gizmos from anywhere |
OnDrawGizmosSelected() | Use if(isDevEnvironment()) to filter |
| Only in Editor | Visible in browser (filter for production!) |
Available methods:
DrawArrow- Arrow between two pointsDrawBox/DrawBox3- Wireframe boxesDrawDirection- Direction arrow from originDrawLine- Line between two pointsDrawRay- Infinite ray from originDrawSphere/DrawWireSphere- Solid/wireframe spheres
Useful Utility Methods
Common Unity Methods
| Unity (C#) | Needle Engine (TypeScript) |
|---|---|
| Check if in Editor | #if UNITY_EDITOR |
| Get URL parameter | N/A |
| Check mobile device | Application.isMobilePlatform |
| Check iOS | Application.platform == RuntimePlatform.IPhonePlayer |
Example:
import { getParam, isDevEnvironment, DeviceUtilities } from "@needle-tools/engine";
// Check URL parameter
if (getParam("debug")) {
console.log("Debug mode enabled");
}
// Platform detection
if (DeviceUtilities.isMobileDevice()) {
// Mobile-specific logic
}
// Development vs production
if (isDevEnvironment()) {
// Show debug UI, gizmos, etc.
}Read more about Platform Detection
Quick Reference Cheat Sheet
| Task | Unity (C#) | Needle Engine (TypeScript) |
|---|---|---|
| Component | MonoBehaviour | Behaviour |
| GameObject | GameObject | Object3D |
| Transform | transform.position | this.gameObject.position |
| World Position | transform.position | this.gameObject.worldPosition |
| Find Component | GetComponent<T>() | getComponent(T) |
| Time | Time.deltaTime | this.context.time.deltaTime |
| Input | Input.GetMouseButtonDown(0) | this.context.input.getPointerDown(0) |
| Raycast | Physics.Raycast(...) | this.context.physics.raycast(...) |
| Debug Log | Debug.Log(...) | console.log(...) |
| Gizmos | OnDrawGizmos() | Gizmos.Draw...() |
| Vector Add | vector += other | vector.add(other) |
| Vector Multiply | vector *= 2 | vector.multiplyScalar(2) |
What's Next?
Continue learning:
- Working with Unity Integration - Unity workflow and glTF export
- Create Components - Build interactive components
- Lifecycle Methods - Complete lifecycle API
Reference:
- TypeScript Essentials - Core TypeScript/JavaScript concepts
- Component Reference - Built-in components
- Scripting Examples - Code examples