Guides /Coming from Unity

Guides

Coming from Unity

A side-by-side tour for Unity developers. Map Update, Time.deltaTime, Input and Debug.Log onto Codetoy.

If you know Unity, you already know most of Codetoy. Think of it as one MonoBehaviour with Update() and OnDrawGizmos(), running in the browser: no scenes, prefabs or inspector, and instant iteration.

It's not a Unity replacement. It's a place to prototype a mechanic in five minutes, explore some math, or share a runnable idea with a link, then take it back to Unity.

Which language?

C# will feel like home, and every snippet here works in it. Switch to AssemblyScript in the sidebar if you want to publish your prototype as a build or Discord Activity; the concepts are identical.

#The mental model

Unity Codetoy
GameObjects and components Plain variables and classes in one file
Scene hierarchy None. You draw everything yourself.
Renderer components draw for you Immediate mode: you draw every frame
Camera clears the screen You clear it (paint a background)
World units, Y up Pixels, origin top-left, Y down (like UI)
Physics engine Write your own (it's simple for 2D)

#The game loop

Unity
void Update() {
    transform.position += velocity * Time.deltaTime;
}
Codetoy
let x: f64 = 0
const speed: f64 = 150

export function update(): void {
  x += speed * deltaTime()
  circle(x, screen.centerY(), 20)
}
double x = 0;
double speed = 150;

Screen.OnUpdate(() => {
    x += speed * Screen.DeltaTime;
    Canvas.Circle(x, Screen.CenterY, 20);
});
x = 0.0
speed = 150.0

def update():
    global x
    x += speed * deltaTime()
    circle(x, centerY(), 20)

export function update()Screen.OnUpdate()def update() runs once per frame like Update(), and deltaTime()Screen.DeltaTimedeltaTime() is Time.deltaTime. There's one loop for the whole program rather than one per component.

#Drawing instead of instantiating

In Unity you create objects once and the engine renders them. In Codetoy nothing exists unless you draw it this frame, which is closer to OnDrawGizmos() or immediate-mode GUI.

Unity
Instantiate(circlePrefab, position, Quaternion.identity);
Codetoy
export function update(): void {
  fill(30, 30, 30)
  rect(0, 0, screen.width(), screen.height())   // clear: Unity does this for you
  circle(x, y, 30)                              // draw it every frame
}
Screen.OnUpdate(() => {
    Canvas.Fill(30, 30, 30);
    Canvas.Rect(0, 0, Screen.Width, Screen.Height);   // clear: Unity does this for you
    Canvas.Circle(x, y, 30);                          // draw it every frame
});
def update():
    fill(30, 30, 30)
    rect(0, 0, width(), height())   # clear: Unity does this for you
    circle(x, y, 30)                # draw it every frame

To have "many objects", keep a list and draw each item in a loop. See Spawning without GameObjects.

#Cheat sheet

Unity Codetoy
Update() export function update()Screen.OnUpdate()def update()
Time.deltaTime deltaTime()Screen.DeltaTimedeltaTime()
Time.time elapsedTime()not in C#elapsedTime()
Input.mousePosition input.mouseX()Input.MouseXmouseX(), input.mouseY()Input.MouseYmouseY()
Input.GetMouseButton(0) input.isMouseDown()Input.IsMouseDown()isMouseDown()
Input.GetMouseButtonDown(0) export function onMouseDown()Input.OnMouseDown()def mouseDown()
Input.GetKey(KeyCode.A) input.isKeyDown()Input.IsKeyDown()isKeyDown() with "a"
Input.GetKeyDown(KeyCode.Space) export function onKeyDown()Input.OnKeyDown()def keyDown() with " "
Screen.width / Screen.height screen.width()Screen.Widthwidth() / screen.height()Screen.Heightheight()
Debug.Log / LogWarning / LogError console.log()Console.Log()print() / console.warn()Console.Warn()not in Python / console.error()Console.Error()print(..., file=sys.stderr)
Gizmos.DrawWireSphere noFill()Canvas.NoFill()noFill() + circle()Canvas.Circle()circle()
Mathf.Sin, Mathf.Atan2 Math.sin()Math.Sin()math.sin(), Math.atan2()Math.Atan2()math.atan2()
Random.value Math.random()Random.Shared.NextDouble()random.random()

#Input: polling and events

Unity
if (Input.GetKey(KeyCode.W)) y += speed * Time.deltaTime;
if (Input.GetKeyDown(KeyCode.Space)) Jump();
Codetoy
export function update(): void {
  if (input.isKeyDown("w")) y -= speed * deltaTime()   // Y is down!
}

export function onKeyDown(key: string): void {
  if (key == " ") jump()
}
Screen.OnUpdate(() => {
    if (Input.IsKeyDown("w")) y -= speed * Screen.DeltaTime;   // Y is down!
});

Input.OnKeyDown((string key) => {
    if (key == " ") Jump();
});
def update():
    global y
    if isKeyDown("w"):
        y -= speed * deltaTime()   # Y is down!

def keyDown(key):
    if key == " ":
        jump()

Keys use browser names ("a", "ArrowUp", " "), not KeyCode. See the Input API.

#State without components

There's no inspector or serialization. Treat the top of your file like a component's fields:

let playerX: f64 = 200
let playerY: f64 = 200
let health = 100
let angle: f64 = 0
double playerX = 200;
double playerY = 200;
int health = 100;
double angle = 0;
player_x = 200.0
player_y = 200.0
health = 100
angle = 0.0

Classes, structs, records, LINQ and generics all work. With top-level statements, declare your variables above any type declarations. See Language Notes.

#Spawning without GameObjects

Unity
void OnMouseDown() { Instantiate(bulletPrefab, transform.position, transform.rotation); }
Codetoy
class Bullet { constructor(public x: f64, public y: f64) {} }
const bullets = new Array<Bullet>()

export function onMouseDown(button: i32): void {
  bullets.push(new Bullet(input.mouseX(), input.mouseY()))
}

export function update(): void {
  fill(20, 20, 30)
  rect(0, 0, screen.width(), screen.height())
  fill(255, 220, 80)
  for (let i = 0; i < bullets.length; i++) {
    const b = bullets[i]
    b.y -= 400 * deltaTime()
    circle(b.x, b.y, 5)
  }
}
var bullets = new List<(double X, double Y)>();

Input.OnMouseDown((int button) => bullets.Add((Input.MouseX, Input.MouseY)));

Screen.OnUpdate(() => {
    Canvas.Fill(20, 20, 30);
    Canvas.Rect(0, 0, Screen.Width, Screen.Height);
    Canvas.Fill(255, 220, 80);
    for (int i = 0; i < bullets.Count; i++) {
        var b = bullets[i];
        b.Y -= 400 * Screen.DeltaTime;
        bullets[i] = b;
        Canvas.Circle(b.X, b.Y, 5);
    }
    bullets.RemoveAll(b => b.Y < -10);   // "Destroy" off-screen bullets
});
bullets = []

def mouseDown(button):
    bullets.append([mouseX(), mouseY()])

def update():
    fill(20, 20, 30)
    rect(0, 0, width(), height())
    fill(255, 220, 80)
    for b in bullets:
        b[1] -= 400 * deltaTime()
        circle(b[0], b[1], 5)
    bullets[:] = [b for b in bullets if b[1] > -10]   # "Destroy" off-screen bullets

#Debugging

console.log()Console.Log()print(), console.warn()Console.Warn()not in Python and console.error()Console.Error()print(..., file=sys.stderr) print to the console under the editor, like Debug.Log. For per-frame values, a debug overlay drawn with text()Canvas.Text()text() is easier to read than a flood of logs:

fill(255, 80, 80)
font("monospace", 12)
text("FPS " + Math.round(screen.fps()).toString(), 10, 10)
Canvas.Fill(255, 80, 80);
Canvas.Font("monospace", 12);
Canvas.Text($"FPS {Screen.FPS:F0}  pos {playerX:F1}, {playerY:F1}", 10, 10);
fill(255, 80, 80)
font("monospace", 12)
text(f"FPS {fps():.0f}  pos {player_x:.1f}, {player_y:.1f}", 10, 10)

#Codetoy or Unity?

Reach for Codetoy to: prototype a mechanic in minutes, visualize vectors and easing, teach a concept with something runnable, or share an idea as a link.

Move to Unity when you need: 3D, physics, an asset pipeline, animation tools, multiple scenes, or console and mobile builds.

Ready for a bigger example? Catch the stars is a complete mini-game in every language.

Esc