Discuss Scratch

Ricky-Jan
Scratcher
11 posts

Surface2D - a 2D Soft-body Physics Engine for Games

Thank you very much for your support and for using the Surface2D Physics Engine
This guideline explains how to use the physics engine, and is roughly divided into several sections: how to create objects, the mouse interaction method, how to create joints between objects, how to access attributes of objects, and how to adjust settings.

Getting Started
Download or remix the package to start using the library
Surface2D v1.0.0 pakage: https://scratch.mit.edu/projects/1328726899/

Example demotration of the physics engine:
Surface2D v1.0.0 demo: https://scratch.mit.edu/projects/1320903729/

Things You Need to Know
  1. The angle coordinate system of this physics engine is based on the polar coordinate system, and the unit is degrees. Therefore, counterclockwise rotation is considered positive, which is opposite to the angle system used in Scratch.
  2. Naming convention of Surface2D variables:
    _ represents a constant/static variable.
    # represents an index.
    Variables or lists starting with an uppercase letter are shared “for all sprites”
    Variables or lists starting with a lowercase letter are “for this sprite only”.
  3. World Space vs Local Space
    In Surface2D, coordinates and vectors can exist in either world space or local space:
    local space: The position/vector that is relative to the origin (0, 0). All vertices or anchor points of the object are defined relative to this internal coordinate system.
    world space: The position/vector represents the absolute coordinates within the entire game world. These coordinates are subject to global transformations, such as camera movement and zoom scales

Launcher
To start using the physics engine, we need to locate the Launcher, which will also serve as your primary development environment.
The Launcher is in the sprite “Your Sprite”, divided into an initialize section and an update section. If you wish to add objects, please do so only after Surface2D has finished initializing (as the initialization process includes deleting all existing objects).
when green flag clicked
initialize
broadcast [initialize surface2d v] and wait
forever
erase all
broadcast [update surface2d v]
broadcast [render surface2d v]
end

Creating Objects

new boundary with width:()height:()margin:()
create a boundary from the screen center
width - the width of the boundary box, unit is pixel
height - the height of the boundary box, unit is pixel
margin - the thickness of the boundary box, unit is pixel
Here's the recommendation of useage:
new boundary with width: ([_screen width v] of [Settings v])height: ([_screen height v] of [Settings v])margin:(100)

new rect at x:()y:()width:()height()direction:()mass:()stiffness:()
create a rectangle soft-body
x, y - the world cooridinate (x, y) of the center of rectangle, unit is pixel.
width, height - the width and height of the rectangle, unit is pixel
direction - the direction of the rectangle, unit is degree (0.0 ~ 360.0)
density - the mass of each nodes on the rectangle, if you want to fix the rectangle, just set it to 0
stiffness - the softness of the rectangle (recommend 0.4 ~ 1)

new regular polygon at x:()y:()radius:()sides:()mass:()stiffness:()
create a regular polygon soft-body
x, y - the world cooridinate (x, y) of the center of regular polygon, unit is pixel
radius - the size of the regular polygon
sides - the number of sides of the regular polygon
mass - the mass of the regular polygon, if you want to fix the regular polygon, just set it to 0
stiffness - the softness of the regular polygon (recommend 0.4 ~ 1)

create a customize polygon
For any convex polygon, please follow these rules for creation:
This method only supports convex polygons, and the nodes must be defined in counter-clockwise (CCW) order.

Below is the example code for creating a trapezoid:
define new trapezoid at x:(x)y:(y)mass:(mass)stiffness:(stiffness)
new node with local x:(-20)y:(20)mass:(mass)
new node with local x:(-40)y:(-20)mass:(mass)
new node with local x:(40)y:(-20)mass:(mass)
new node with local x:(20)y:(20)mass:(mass)
new body at x:(x)y:(y)stiffness:(stiffness)

Add this self-designed function to the launcher, and you can see a trapezoid soft body:
when green flag clicked
initialize
broadcast [initialize surface2d v] and wait
new boundary with width: ([_screen width v] of [Settings v])height: ([_screen height v] of [Settings v])margin:(60)
new trapezoid at x:(100)y:(100)mass:(1)stiffness:(1)
forever
erase all
drag body with stiffness:(0.05)
broadcast [update surface2d v]
broadcast [render surface2d v]
end

create a compound object
To create a concave object, the method is to seperate the concave compound oobject into combination of convex objects, the engine provides the following methods:
begin compound body
Initialize the compound datas

end compound body at x:()y:()
convert the data into the atual body and create the compound body

new compound rect at local x:()y:()width:()height:()direction:()mass:()stiffness()
add a rectangle to the ccompound body
local x - the local position x of the rectangle relative to the compound position x, unit is pixel
local y - the local position y of the rectangle relative to the compound position y, unit is pixel
width - the width of the rectangle, unit is pixel
height - the height of the rectangle, unit is pixel
direction - the direction of the rectangle, unit is degree (0.0 ~ 360.0)
mass - the mass of the rectangle, a number greater than 0
stiffness - the softness of the rectangle (recommend 0.4 ~ 1)

new compound regular polygon at local x:()y:()sides:()radius:()mass:()stiffness()
add a regular polygon to the ccompound body
local x - the local position x of the regular polygon relative to the compound position x, unit is pixel
local y - the local position y of the regular polygon relative to the compound position y, unit is pixel
sides - the sides of the regular polygon, the integer number greater than 3
radius - the size of the regular polygon, a number greater than 0
mass - the mass of the regular polygon, a number greater than 0
stiffness - the softness of the regular polygon (recommend 0.4 ~ 1)

Here's the example to make a compound dumbbel object:
define new dumbbel at x:(x)y:(y)mass:(mass)stiffness:(stiffness)
begin compound body
new compound regular polygon at local x:(-70)y:(0)sides:(8)radius:(40)mass:(mass)stiffness:(stiffness)
new compound rect at local x:(0)y(0)width:(65)height:(30)direction:(0)mass:(mass):stiffness:(stiffness)
new compound regular polygon at local x:(70)y:(0)sides:(8)radius:(40)mass:(mass)stiffness:(stiffness)
end compound body at x:(x)y:(y)

Add this self-designed function to the launcher, and you can see a compound dumbbel object:
when green flag clicked
initialize
broadcast [initialize surface2d v] and wait
new boundary with width: ([_screen width v] of [Settings v])height: ([_screen height v] of [Settings v])margin:(100)
new dumbbel at x:(100)y:(100)mass:(1)stiffness:(0.5)
forever
erase all
drag body with stiffness:(0.05)
broadcast [update surface2d v]
broadcast [render surface2d v]
end

costumized compound shape
For any convex polygon in the compound, please follow these rules for creation:
This method only supports convex polygons, and the nodes must be defined in counter-clockwise (CCW) order

begin compound nodes with sides:()local x:()y:()mass:()stiffness:()
Initialize the abstract body in compound body
sides - the sides of the abstract body
local x - the local x position of the body relative to the compound position x
local y - the local y position of the body relative to the compound position y
mass - the mass of the body
stiffness - the softness of the body

end compound node
Convert the compound body datas into atual compound body object

new compound node at local x:()y:()
add the compound node to the body
local x - the node position x relative to the body position x
local y - the node position y relative to the body position y

Here's the example code to create a compound heart object:
define new heart at x:(x) y:(y) mass:(mass) stiffness:(stiffness)
begin compound body
begin compound nodes with sides:(7) local x:(0) y:(0) mass:(mass) stiffness:(stiffness)
new compound node at local x:(0)y:(20)
new compound node at local x:(-20) y:(40)
new compound node at local x:(-45) y:(40)
new compound node at local x:(-60) y:(20)
new compound node at local x:(-60) y:(0)
new compound node at local x:(-50) y:(-20)
new compound node at local x:(0) y:(-60)
end compound node
begin compound nodes with sides:(7) local x:(0) y:(0) mass:(mass) stiffness:(stiffness)
new compound node at local x:(0)y:(20)
new compound node at local x:(0) y:(-60)
new compound node at local x:(50) y:(-20)
new compound node at local x:(60) y:(0)
new compound node at local x:(60) y:(20)
new compound node at local x:(45) y:(40)
new compound node at local x:(20) y:(40)
end compound node
end compound body at x:(x)y:(y)

Add this self-designed function to the launcher, and you can see a compound heart object:
when green flag clicked
initialize
broadcast [initialize surface2d v] and wait
new boundary with width: ([_screen width v] of [Settings v])height: ([_screen height v] of [Settings v])margin:(100)
new heart at x:(100)y:(100)mass:(1)stiffness:(1)
forever
erase all
drag body with stiffness:(0.03)
broadcast [update surface2d v]
broadcast [render surface2d v]
end

Mouse Interaction
is mouse-pointer touching object?
Allow the mouse to touch objects, this method is provided:
is point x:()y:()inside body:()?
check the point is inside body with given body index
point x - the point position x to check if inside the body or not
point y - the point position y to check if inside the body or not
# body - the target body index

and the result would return to this boolean variable:
(point in body?)
true - the point is inside the target body
false - the point is not inside the target body

Here is the example code to check if the mouse pointer is inside the body:
when green flag clicked
initialize
broadcast [initialize surface2d v] and wait
new regular polygon at x:(0)y:(0)radius:(50)sides:(5)mass:(0)stiffness:(0)
forever
erase all
is point x:(((mouse x) / (Zoom)) + (Camera x)) y:(((mouse y) / (Zoom)) + (Camera y)) inside body:(1) ?
if <(point in body?) = [true]> then
say [touched]
else
say [not touching]
end
broadcast [update surface2d v]
broadcast [render surface2d v]
end

select object
get target body with point x:()y:()
To get the target body if the body touching the specific point by given point position
point x - the point position x for body to detect
point y - the point position y for body to detect

and the result would return to this variable:
(# target body)
0 - the point is not touching any bodies
other number than 0 - the body index which touched the point

Here is the example code to selected the body using mouse-pointer:
when green flag clicked
initialize
broadcast [initialize surface2d v] and wait
new regular polygon at x:(-100)y:(100)radius:(50)sides:(3)mass:(0)stiffness:(0)
new regular polygon at x:(-100)y:(-100)radius:(50)sides:(4)mass:(0)stiffness:(0)
new regular polygon at x:(100)y:(-100)radius:(50)sides:(5)mass:(0)stiffness:(0)
new regular polygon at x:(100)y:(100)radius:(50)sides:(6)mass:(0)stiffness:(0)
forever
erase all
get target body with point x:(((mouse x) / (Zoom)) + (Camera x)) y:(((mouse y) / (Zoom)) + (Camera y))
say (# target body)
broadcast [update surface2d v]
broadcast [render surface2d v]
end

drag object
drag body with stiffness:()
To drag the selected body
stiffness - the link strength to drag the body (0.0 ~ 1.0)

Object Coloring and Disable Collision
To color an object, these two method are provided:
fill body:() with hexadecimal color:()
Fill the body with color based on hexadecimal color code
# body - body index
hex color - hexadecimal color code (ex: 0x50FF0000)

fill body:() with r:() g:() b:() a:()
Fill the body with color based on RGBA color system
# body - body index
r - red value (0 ~ 255)
g - green value (0 ~ 255)
b - blue value (0 ~ 255)
a - alpha/transparency (0 ~ 255)

set collision layer of body:() layer:() can acess layers:()()()()()()
sets the collision layer of an object and specifies which layers it can interact with.
# body - the index of the object
layer - the collision layer assigned to the object (default is 1)
can access layers - the layers that this object is allowed to collide with (make sure that the layers an object can access also include its own layer)

Joints
There are few joint types: distance joint, revolute joint and mouse joint are provided

new distance joint between body A:()body B:()anchor local pos A:()()B:()()stiffness:()
create a distance joint between objects, fixes two objects at a specific distance apart, and can also adjust stiffness to behave like a spring. Applications like hanging lamps, bridges, signboards, etc.

# body A, # body B - the objects index that you want to connect with
anchor local pos A - the anchor local position relative to the object A position
anchor local pos B - the anchor local position relative to the object B position
stiffness - the stiffness of the joint (0.0 ~ 1.0)

new revolute joint between body A:()body B:()anchor local pos A:()()B:()()stiffness:() angle lower limit:()upper limit:()
create a revolute joint between objects, make objects can rotate with the specific point. Application like windmills, hinges, etc.

# body A, # body B - the objects index that you want to connect with
anchor local pos A - the anchor local position relative to the object A position
anchor local pos B - the anchor local position relative to the object B position
stiffness - the stiffness of the joint (0.0 ~ 1.0)
angle lower limit - the CCW degree limit of the angle (0 ~ 180), can leave as blank if not used
angle upper limit - the CW degree limit of the angle (0 ~ 180), can leave as blank if not used

new mouse joint with target body:()point position:()()anchor local pos:()()stiffness:()
create a spring like joint to the target object, to hang or drag objects. Application like dragging objects, elastic suspended objects, etc…

target body – The soft body index which connected to the mouse joint
point position – The target position in world coordinates
anchor local pos – The local anchor position on the rigid body
stiffness – how strongly the object follows the target position

destroy joint:()
Remove the joint by the given index
# joint - joint index that to be removed

Common Attribute
The following are commonly used attributes, which can be used to retrieve the current state of a soft ody:
(item (5) of [Bodies is colliding? v] :: list)
boolean value. Indicates whether the 5th body is colliding with another body. Returns “true” if it is colliding, otherwise “false”

(item (5) of [Bodies centroid x v] :: list)
The geometry center of mass position x of 5th body, can treat as the center x of the body

(item (5) of [Bodies centroid y v] :: list)
The geometry center of mass position y of 5th body, can treat as the center y of the body


(item (5) of [Bodies node index v] :: list)
The starting node index of the 5th body

(item (5) of [Bodies num nodes v] :: list)
The number of node of the 5th body

(item (5) of [Nodes pos x v] :: list)
The position x of the 5th node

(item (5) of [Nodes pos y v] :: list)
The position y of the 5th node

Physics Settings
In the Sprite “Settings”, you can find this custom block definition, which includes several parameters that can be adjusted according to your project.

define initialize physics settings

set [_gravity x v] to []
The gravitational acceleration in the x direction. If set too high, it may cause instability in stacked objects or result in physics tunneling (objects passing through each other)

set [_gravity y v] to []
The gravitational acceleration in the y direction. If set too high, it may cause instability in stacked objects or result in physics tunneling (objects passing through each other)

set [_friction v] to []
The friction between soft bodies, a number greater than 0

set [_time step v] to []
The physics update time step. It is usually set based on the average FPS of the development environment


Rendering Setting
The rendering settings are located in the sprite “Renderer”, where you can configure the style of the built-in renderer. you can find this custom block definition, which includes several parameters that can be adjusted according to your project:

define reset render option list

define reset render option information list
Where you can set the color and pen size, what to draw or what to not draw based on your preference

Enjoy and good luck on your development!

Last edited by Ricky-Jan (June 5, 2026 07:28:14)

FunFanGuy
Scratcher
10 posts

Surface2D - a 2D Soft-body Physics Engine for Games

Should we make GitHub pages for projects and remixes? I just think it would be cool if we had another way to change up projects and publish as long as we can get scratch to support other coding languages.
E_MachineVR
Scratcher
3 posts

Surface2D - a 2D Soft-body Physics Engine for Games

YOOOOOOOOOOOOOOOOOOOOOOOOO THAT'S SO COOOOL!!! HOWWWW‽‽‽‽‽‽‽‽‽‽‽‽‽‽‽‽‽‽‽‽‽‽‽‽‽‽‽‽‽‽‽‽‽‽‽‽‽‽‽‽‽

Powered by DjangoBB