at Graph
3 folders
122 files
feat: GPU particles implementation (#3212)
GPU Particle Implementation! Added GPU particle implementation for MANY MANY particles in the simulation, similar to the existing CPU particle implementation. Note `maxParticles` is new for GPU particles.
var particles = new ex.GpuParticleEmitter({
pos: ex.vec(300, 500),
maxParticles: 10_000,
emitRate: 1000,
radius: 100,
emitterType: ex.EmitterType.Circle,
particle: {
beginColor: ex.Color.Orange,
endColor: ex.Color.Purple,
focus: ex.vec(0, -400),
focusAccel: 1000,
startSize: 100,
endSize: 0,
life: 3000,
minSpeed: -100,
maxSpeed: 100,
angularVelocity: 2,
randomRotation: true,
transform: ex.ParticleTransform.Local
}
});
feat: Add ex.BezierCurve and CurveBy/CurveTo actions (#3282)
https://github.com/user-attachments/assets/e2bdff59-7551-4652-a4ba-7ce78d7eeaf8
This PR implements new CurveTo/CurveBy action for moving actors in a Bezier curve
```typescript
const curve = new ex.BezierCurve({
controlPoints: [ex.vec(0, 700), ex.vec(100, -300), ex.vec(150, 800), ex.vec(500, 100)],
quality: 10
});
actor.actions.repeatForever((ctx) => {
ctx.curveTo({
controlPoints: [ex.vec(100, -300), ex.vec(150, 800), ex.vec(500, 100)],
durationMs: 6000
});
ctx.curveBy({
controlPoints: [ex.vec(100, 0), ex.vec(-100, 0), ex.vec(0, 300)],
durationMs: 1000
});
ctx.curveTo({
controlPoints: [ex.vec(150, 800), ex.vec(100, -300), ex.vec(0, 700)],
durationMs: 6000
});
});
```
change: GraphicsGroup no longer centers with anchors (#2966)
`useAnchor: false` The graphics members are positioned from the top left
```typescript
const graphicGroup = new ex.GraphicsGroup({
useAnchor: false,
members: [
{
graphic: heartImage.toSprite(),
offset: ex.vec(0, 0),
},
{
graphic: heartImage.toSprite(),
offset: ex.vec(0, 16),
},
{
graphic: heartImage.toSprite(),
offset: ex.vec(16, 16),
},
{
graphic: heartImage.toSprite(),
offset: ex.vec(16, 0),
},
],
});
```

`useAnchor: true` The graphics members' total combined bounds are centered by the actor's anchor (.5, .5) by default
```typescript
const graphicGroup = new ex.GraphicsGroup({
useAnchor: true,
members: [
{
graphic: heartImage.toSprite(),
offset: ex.vec(0, 0),
},
{
graphic: heartImage.toSprite(),
offset: ex.vec(0, 16),
},
{
graphic: heartImage.toSprite(),
offset: ex.vec(16, 16),
},
{
graphic: heartImage.toSprite(),
offset: ex.vec(16, 0),
},
],
});
```

feat: Implement Uniform Buffers Object Support (#3360)
This PR implements UBO support in Excalibur
```typescript
var material = game.graphicsContext.createMaterial({
name: 'light-material',
fragmentSource: `#version 300 es
precision mediump float;
struct Light {
vec2 pos;
float radius;
vec4 color;
};
layout(std140) uniform Lighting {
Light lights[2];
};
in vec2 v_uv;
out vec4 color;
void main() {
float distanceToLights = 1.0;
vec4 finalColor = vec4(0.0, 0.0, 0.0, 1.0);
for (int i = 0; i < 2; i++) {
float dist = length(lights[i].pos - v_uv);
dist = smoothstep(lights[i].radius-.2, lights[i].radius+.2, dist);
finalColor += lights[i].color * (1.0 - dist);
}
color = finalColor;
// premultiply alpha
color.rgb = color.rgb * color.a;
}`,
uniforms: {
// prettier-ignore
Lighting: new Float32Array([
0.5, 0.5, 0.1, 0.1, // light 1 pos
0, 1, 0, 1, // light 1 color
1, 1, 0.1, 0.1, // light2 pos
0, 0, 1, 1 // light 2 color
])
}
}) as ex.Material;
actor.graphics.material = material;
ex.coroutine(
game,
function* () {
let time = 0;
while (true) {
const elapsed = yield;
time += elapsed / 1000;
const x1 = Math.cos(time);
const y1 = Math.sin(time);
const x2 = Math.cos(-time);
const y2 = Math.sin(-time);
// prettier-ignore
material.uniforms.Lighting = new Float32Array([
0.2 * x1 + 0.2, 0.2 * y1 + 0.2, 0.1, 0,
0, 1, 0, 1,
0.5 * x2 + 0.5, 0.5 * y2 + 0.5, 0.1, 0,
0, 0, 1, 1
]); }
}.bind(this)
);
game.start(new ex.Loader([tex]));
```
Additionally there are some DX enhancements to working with uniforms
- You can now set uniform values directly on materials/shader instances and will be upload on next `.use()`
`material.uniforms.Lighting = ...`
feat: Nine-slice Graphic (#3241)
- Added a new `ex.NineSlice` `Graphic` for creating arbitrarily resizable rectangular regions, useful for creating UI, backgrounds, and other resizable elements.
```typescript
var nineSlice = new ex.NineSlice({
width: 300,
height: 100,
source: inputTile,
sourceConfig: {
width: 64,
height: 64,
topMargin: 5,
leftMargin: 7,
bottomMargin: 5,
rightMargin: 7
},
destinationConfig: {
drawCenter: true,
horizontalStretch: ex.NineSliceStretch.Stretch,
verticalStretch: ex.NineSliceStretch.Stretch
}
});
actor.graphics.add(nineSlice);
```
feat: Implement Uniform Buffers Object Support (#3360)
This PR implements UBO support in Excalibur
```typescript
var material = game.graphicsContext.createMaterial({
name: 'light-material',
fragmentSource: `#version 300 es
precision mediump float;
struct Light {
vec2 pos;
float radius;
vec4 color;
};
layout(std140) uniform Lighting {
Light lights[2];
};
in vec2 v_uv;
out vec4 color;
void main() {
float distanceToLights = 1.0;
vec4 finalColor = vec4(0.0, 0.0, 0.0, 1.0);
for (int i = 0; i < 2; i++) {
float dist = length(lights[i].pos - v_uv);
dist = smoothstep(lights[i].radius-.2, lights[i].radius+.2, dist);
finalColor += lights[i].color * (1.0 - dist);
}
color = finalColor;
// premultiply alpha
color.rgb = color.rgb * color.a;
}`,
uniforms: {
// prettier-ignore
Lighting: new Float32Array([
0.5, 0.5, 0.1, 0.1, // light 1 pos
0, 1, 0, 1, // light 1 color
1, 1, 0.1, 0.1, // light2 pos
0, 0, 1, 1 // light 2 color
])
}
}) as ex.Material;
actor.graphics.material = material;
ex.coroutine(
game,
function* () {
let time = 0;
while (true) {
const elapsed = yield;
time += elapsed / 1000;
const x1 = Math.cos(time);
const y1 = Math.sin(time);
const x2 = Math.cos(-time);
const y2 = Math.sin(-time);
// prettier-ignore
material.uniforms.Lighting = new Float32Array([
0.2 * x1 + 0.2, 0.2 * y1 + 0.2, 0.1, 0,
0, 1, 0, 1,
0.5 * x2 + 0.5, 0.5 * y2 + 0.5, 0.1, 0,
0, 0, 1, 1
]); }
}.bind(this)
);
game.start(new ex.Loader([tex]));
```
Additionally there are some DX enhancements to working with uniforms
- You can now set uniform values directly on materials/shader instances and will be upload on next `.use()`
`material.uniforms.Lighting = ...`
feat: Implement Uniform Buffers Object Support (#3360)
This PR implements UBO support in Excalibur
```typescript
var material = game.graphicsContext.createMaterial({
name: 'light-material',
fragmentSource: `#version 300 es
precision mediump float;
struct Light {
vec2 pos;
float radius;
vec4 color;
};
layout(std140) uniform Lighting {
Light lights[2];
};
in vec2 v_uv;
out vec4 color;
void main() {
float distanceToLights = 1.0;
vec4 finalColor = vec4(0.0, 0.0, 0.0, 1.0);
for (int i = 0; i < 2; i++) {
float dist = length(lights[i].pos - v_uv);
dist = smoothstep(lights[i].radius-.2, lights[i].radius+.2, dist);
finalColor += lights[i].color * (1.0 - dist);
}
color = finalColor;
// premultiply alpha
color.rgb = color.rgb * color.a;
}`,
uniforms: {
// prettier-ignore
Lighting: new Float32Array([
0.5, 0.5, 0.1, 0.1, // light 1 pos
0, 1, 0, 1, // light 1 color
1, 1, 0.1, 0.1, // light2 pos
0, 0, 1, 1 // light 2 color
])
}
}) as ex.Material;
actor.graphics.material = material;
ex.coroutine(
game,
function* () {
let time = 0;
while (true) {
const elapsed = yield;
time += elapsed / 1000;
const x1 = Math.cos(time);
const y1 = Math.sin(time);
const x2 = Math.cos(-time);
const y2 = Math.sin(-time);
// prettier-ignore
material.uniforms.Lighting = new Float32Array([
0.2 * x1 + 0.2, 0.2 * y1 + 0.2, 0.1, 0,
0, 1, 0, 1,
0.5 * x2 + 0.5, 0.5 * y2 + 0.5, 0.1, 0,
0, 0, 1, 1
]); }
}.bind(this)
);
game.start(new ex.Loader([tex]));
```
Additionally there are some DX enhancements to working with uniforms
- You can now set uniform values directly on materials/shader instances and will be upload on next `.use()`
`material.uniforms.Lighting = ...`
feat: Implement Uniform Buffers Object Support (#3360)
This PR implements UBO support in Excalibur
```typescript
var material = game.graphicsContext.createMaterial({
name: 'light-material',
fragmentSource: `#version 300 es
precision mediump float;
struct Light {
vec2 pos;
float radius;
vec4 color;
};
layout(std140) uniform Lighting {
Light lights[2];
};
in vec2 v_uv;
out vec4 color;
void main() {
float distanceToLights = 1.0;
vec4 finalColor = vec4(0.0, 0.0, 0.0, 1.0);
for (int i = 0; i < 2; i++) {
float dist = length(lights[i].pos - v_uv);
dist = smoothstep(lights[i].radius-.2, lights[i].radius+.2, dist);
finalColor += lights[i].color * (1.0 - dist);
}
color = finalColor;
// premultiply alpha
color.rgb = color.rgb * color.a;
}`,
uniforms: {
// prettier-ignore
Lighting: new Float32Array([
0.5, 0.5, 0.1, 0.1, // light 1 pos
0, 1, 0, 1, // light 1 color
1, 1, 0.1, 0.1, // light2 pos
0, 0, 1, 1 // light 2 color
])
}
}) as ex.Material;
actor.graphics.material = material;
ex.coroutine(
game,
function* () {
let time = 0;
while (true) {
const elapsed = yield;
time += elapsed / 1000;
const x1 = Math.cos(time);
const y1 = Math.sin(time);
const x2 = Math.cos(-time);
const y2 = Math.sin(-time);
// prettier-ignore
material.uniforms.Lighting = new Float32Array([
0.2 * x1 + 0.2, 0.2 * y1 + 0.2, 0.1, 0,
0, 1, 0, 1,
0.5 * x2 + 0.5, 0.5 * y2 + 0.5, 0.1, 0,
0, 0, 1, 1
]); }
}.bind(this)
);
game.start(new ex.Loader([tex]));
```
Additionally there are some DX enhancements to working with uniforms
- You can now set uniform values directly on materials/shader instances and will be upload on next `.use()`
`material.uniforms.Lighting = ...`