Add vector swizzling; custom velocity function; per object gravity disabling; max velocities

This commit is contained in:
John Alanbrook 2023-11-06 13:05:27 +00:00
parent 5ee444465e
commit 1a6dbfaa88
9 changed files with 238 additions and 17 deletions

View file

@ -573,14 +573,93 @@ function arrsetelem(str, n)
Object.defineProperty(Array.prototype, str, setelem(n)); Object.defineProperty(Array.prototype, str, setelem(n));
} }
Object.defineProperty(Array.prototype, 'x', setelem(0)); var arr_elems = ['x', 'y', 'z', 'w'];
Object.defineProperty(Array.prototype, 'y', setelem(1)); var quat_elems = ['i', 'j', 'k'];
Object.defineProperty(Array.prototype, 'z', setelem(2)); var color_elems = ['r', 'g', 'b', 'a'];
Object.defineProperty(Array.prototype, 'w', setelem(3));
arrsetelem('r', 0); arr_elems.forEach(function(x, i) { arrsetelem(x,i); });
arrsetelem('g', 1); quat_elems.forEach(function(x, i) { arrsetelem(x,i); });
arrsetelem('b', 2); color_elems.forEach(function(x, i) { arrsetelem(x,i); });
arrsetelem('a', 3);
var nums = [0,1,2,3];
var swizz = [];
for (var i of nums)
for (var j of nums)
swizz.push([i,j]);
swizz.forEach(function(x) {
var str = "";
for (var i of x)
str += arr_elems[i];
Object.defineProperty(Array.prototype, str, {
get() { return [this[x[0]], this[x[1]]]; },
set(j) { this[x[0]] = j[0]; this[x[1]] = j[1]; },
});
str = "";
for (var i of x) str += color_elems[i];
Object.defineProperty(Array.prototype, str, {
get() { return [this[x[0]], this[x[1]]]; },
set(j) { this[x[0]] = j[0]; this[x[1]] = j[1]; },
});
});
swizz = [];
for (var i of nums)
for (var j of nums)
for (var k of nums)
swizz.push([i,j,k]);
swizz.forEach(function(x) {
var str = "";
for (var i of x)
str += arr_elems[i];
Object.defineProperty(Array.prototype, str, {
get() { return [this[x[0]], this[x[1]], this[x[2]]]; },
set(j) { this[x[0]] = j[0]; this[x[1]] = j[1]; this[x[2]] = j[2];},
});
str = "";
for (var i of x) str += color_elems[i];
Object.defineProperty(Array.prototype, str, {
get() { return [this[x[0]], this[x[1]], this[x[2]]]; },
set(j) { this[x[0]] = j[0]; this[x[1]] = j[1]; this[x[2]] = j[2];},
});
});
swizz = [];
for (var i of nums)
for (var j of nums)
for (var k of nums)
for (var w of nums)
swizz.push([i,j,k,w]);
swizz.forEach(function(x) {
var str = "";
for (var i of x)
str += arr_elems[i];
Object.defineProperty(Array.prototype, str, {
get() { return [this[x[0]], this[x[1]], this[x[2]], this[x[3]]];},
set(j) { this[x[0]] = j[0]; this[x[1]] = j[1]; this[x[2]] = j[2]; this[x[3]] = j[3];},
});
str = "";
for (var i of x) str += color_elems[i];
Object.defineProperty(Array.prototype, str, {
get() { return [this[x[0]], this[x[1]], this[x[2]], this[x[3]]];},
set(j) { this[x[0]] = j[0]; this[x[1]] = j[1]; this[x[2]] = j[2]; this[x[3]] = j[3];},
});
});
Object.defineProperty(Array.prototype, 'add', { Object.defineProperty(Array.prototype, 'add', {
value: function(b) { value: function(b) {

View file

@ -560,6 +560,10 @@ component.edge2d = Object.copy(collider2d, {
return spoints; return spoints;
}, },
post() {
this.cpoints = [];
},
sample(n) { sample(n) {
var spoints = this.spoints(); var spoints = this.spoints();
@ -729,12 +733,12 @@ bucket.inputs['C-lm'] = function() {
bucket.inputs['C-lm'].doc = "Add a point to the spline at the mouse position."; bucket.inputs['C-lm'].doc = "Add a point to the spline at the mouse position.";
bucket.inputs['C-M-lm'] = function() { bucket.inputs['C-M-lm'] = function() {
// var idx = grab_from_points(screen2world(Mouse.pos), this.cpoints.map(function(x) {return x.sub(this.gameobject.worldpos()); }, this), 25);
var idx = cmd(59, Mouse.worldpos.sub(this.gameobject.pos), this.cpoints, 250); var idx = cmd(59, Mouse.worldpos.sub(this.gameobject.pos), this.cpoints, 250);
Log.warn(idx);
// var idx = grab_from_points(screen2world(Mouse.pos), this.cpoints.map(function(x) {return x.add(this.gameobject.pos); }, this), 25);
if (idx === -1) return;
this.cpoints.splice(idx, 1); if (idx <= 0 || idx > this.cpoints.length) return;
this.cpoints.splice(idx-1, 1);
}; };
bucket.inputs['C-M-lm'].doc = "Remove point from the spline."; bucket.inputs['C-M-lm'].doc = "Remove point from the spline.";

View file

@ -168,8 +168,11 @@ function ediff(from,to)
if (typeof v === 'undefined') return; if (typeof v === 'undefined') return;
if (Array.isArray(v)) { if (Array.isArray(v)) {
if (!Array.isArray(to[key]) || v.length !== to[key].length) if (!Array.isArray(to[key]) || v.length !== to[key].length) {
ret[key] = Object.values(ediff(v, [])); var r = ediff(v,[]);
if (r) ret[key] = Object.values(r);
return;
}
var diff = ediff(from[key], to[key]); var diff = ediff(from[key], to[key]);
if (diff && !diff.empty) if (diff && !diff.empty)

View file

@ -7,7 +7,18 @@ prototypes.generate_ur('.');
var editor_config = { var editor_config = {
grid_size: 100, grid_size: 100,
ruler_mark_px: 100, ruler_mark_px: 100,
grid_color: [99, 255, 128, 100], grid_color: Color.green.alpha(0.3),
};
var Device = {
pc: [1920,1080],
macbook_m2: [2560,1664],
ds_top: [400,240],
ds_bottom: [320,240],
switch: [1280,720],
ipad_air_m2: [2360,1640],
iphone_se: [1334, 750],
}; };
var configs = { var configs = {
@ -209,6 +220,7 @@ var editor = {
Player.players[0].control(limited_editor); Player.players[0].control(limited_editor);
Register.unregister_obj(this); Register.unregister_obj(this);
editor.dbg_play = Primum.spawn(this.dbg_ur); editor.dbg_play = Primum.spawn(this.dbg_ur);
editor.dbg_play.pos = [0,0];
load("debug.js"); load("debug.js");
}, },
@ -538,6 +550,9 @@ var editor = {
Debug.boundingbox(bb, Color.Editor.select.alpha(0.1)); Debug.boundingbox(bb, Color.Editor.select.alpha(0.1));
Debug.line(bb2points(bb).wrapped(1), Color.white); Debug.line(bb2points(bb).wrapped(1), Color.white);
} }
/* 1920x1080 */
Debug.line(bb2points(cwh2bb(world2screen([0,0]),[1920/Game.camera.zoom,1080/Game.camera.zoom])).wrapped(1), Color.yellow);
if (this.curpanel && this.curpanel.on) if (this.curpanel && this.curpanel.on)
this.curpanel.gui(); this.curpanel.gui();
@ -676,6 +691,32 @@ editor.inputs['C-a'].doc = "Select all objects.";
editor.inputs['C-`'] = function() { editor.openpanel(replpanel); } editor.inputs['C-`'] = function() { editor.openpanel(replpanel); }
editor.inputs['C-`'].doc = "Open or close the repl."; editor.inputs['C-`'].doc = "Open or close the repl.";
editor.inputs.n = function() {
if (editor.selectlist.length !== 1) return;
var o = editor.try_select();
if (!o) return;
if (o === editor.selectlist[0]) return;
if (o.level !== editor.selectlist[0].level) return;
var tpos = editor.selectlist[0].pos;
tpos.x *= -1;
o.pos = tpos;
};
editor.inputs.n.doc = "Set the hovered object's position to mirror the selected object's position on the X axis."
editor.inputs['M-n'] = function()
{
if (editor.selectlist.length !== 1) return;
var o = editor.try_select();
if (!o) return;
if (o === editor.selectlist[0]) return;
if (o.level !== editor.selectlist[0].level) return;
var tpos = editor.selectlist[0].pos;
tpos.y *= -1;
o.pos = tpos;
};
editor.inputs.n.doc = "Set the hovered object's position to mirror the selected object's position on the Y axis."
/* Return if selected component. */ /* Return if selected component. */
editor.inputs['h'] = function() { editor.inputs['h'] = function() {
var visible = true; var visible = true;

View file

@ -34,6 +34,12 @@ var gameobject = {
}; };
this.objects = {}; this.objects = {};
}, },
set max_velocity(x) { cmd(151, this.body, x); },
get max_velocity() { return cmd(152, this.body); },
set max_angularvelocity(x) { cmd(154, this.body, Math.deg2rad(x)); },
get max_angularvelocity() { return Math.rad2deg(cmd(155, this.body)); },
set torque(x) { if (!(x >= 0 && x <= Infinity)) return; cmd(153, this.body, x); },
gscale() { return cmd(103,this.body); }, gscale() { return cmd(103,this.body); },
sgscale(x) { cmd(36,this.body,x) }, sgscale(x) { cmd(36,this.body,x) },
get scale() { get scale() {
@ -99,6 +105,8 @@ var gameobject = {
return q_body(5, this.body); return q_body(5, this.body);
}, },
set gravity(x) { cmd(158,this.body, x); },
get gravity() { return cmd(159,this.body); },
set phys(x) { set_body(1, this.body, x); }, set phys(x) { set_body(1, this.body, x); },
get phys() { return q_body(0,this.body); }, get phys() { return q_body(0,this.body); },
@ -199,9 +207,20 @@ var gameobject = {
components: {}, components: {},
objects: {}, objects: {},
level: undefined, level: undefined,
get_moi() { return q_body(6, this.body); },
set_moi(x) {
if(x <= 0) {
Log.error("Cannot set moment of inertia to 0 or less.");
return;
}
set_body(13, this.body, x);
},
pulse(vec) { set_body(4, this.body, vec);}, pulse(vec) { set_body(4, this.body, vec);},
shove(vec) { set_body(12,this.body,vec);}, shove(vec) { set_body(12,this.body,vec);},
shove_at(vec, at) { set_body(14,this.body,vec,at); },
torque(val) { cmd(153, this.body, val); },
world2this(pos) { return cmd(70, this.body, pos); }, world2this(pos) { return cmd(70, this.body, pos); },
this2world(pos) { return cmd(71, this.body,pos); }, this2world(pos) { return cmd(71, this.body,pos); },
set layer(x) { cmd(75,this.body,x); }, set layer(x) { cmd(75,this.body,x); },
@ -272,12 +291,19 @@ var gameobject = {
register_collide(1, x.collide, x, obj.body, x.shape); register_collide(1, x.collide, x, obj.body, x.shape);
}); });
}, },
pos: [0,0],
angle:0,
velocity:[0,0],
angularvelocity:0,
phys:Physics.static, phys:Physics.static,
flipx:false, flipx:false,
flipy:false, flipy:false,
scale:1, scale:1,
elasticity:0.5, elasticity:0.5,
friction:1, friction:1,
gravity: true,
max_velocity: Infinity,
max_angularvelocity: Infinity,
mass:1, mass:1,
layer:0, layer:0,
worldpos() { return [0,0]; }, worldpos() { return [0,0]; },
@ -520,6 +546,7 @@ var gameobject = {
if (data) if (data)
Object.dainty_assign(obj,data); Object.dainty_assign(obj,data);
if (typeof obj.warmup === 'function') obj.warmup();
if (Game.playing() && typeof obj.start === 'function') obj.start(); if (Game.playing() && typeof obj.start === 'function') obj.start();
return obj; return obj;
@ -586,6 +613,7 @@ gameobject.doc = {
flipy: "Set the object to be flipped on its y axis.", flipy: "Set the object to be flipped on its y axis.",
elasticity: `When two objects collide, their elasticities are multiplied together. Their velocities are then multiplied by this value to find their resultant velocities.`, elasticity: `When two objects collide, their elasticities are multiplied together. Their velocities are then multiplied by this value to find their resultant velocities.`,
friction: `When one object touches another, friction slows them down.`, friction: `When one object touches another, friction slows them down.`,
gravity: 'True if this object should be affected by gravity.',
mass: `The higher the mass of the object, the less forces will affect it.`, mass: `The higher the mass of the object, the less forces will affect it.`,
phys: `Set to 0, 1, or 2, representing static, kinematic, and dynamic.`, phys: `Set to 0, 1, or 2, representing static, kinematic, and dynamic.`,
worldpos: `Function returns the world position of the object.`, worldpos: `Function returns the world position of the object.`,
@ -594,6 +622,9 @@ gameobject.doc = {
rotate: `Function to rotate this object by x degrees.`, rotate: `Function to rotate this object by x degrees.`,
pulse: `Apply an impulse to this body in world coordinates. Impulse is a short force.`, pulse: `Apply an impulse to this body in world coordinates. Impulse is a short force.`,
shove: `Apply a force to this body in world coordinates. Should be used over many frames.`, shove: `Apply a force to this body in world coordinates. Should be used over many frames.`,
shove_at: 'Apply a force to this body, at a position relative to itself.',
max_velocity: 'The max linear velocity this object can travel.',
max_angularvelocity: 'The max angular velocity this object can rotate.',
in_air: `Return true if the object is in the air.`, in_air: `Return true if the object is in the air.`,
on_ground: `Return true if the object is on the ground.`, on_ground: `Return true if the object is on the ground.`,
spawn: `Create an instance of a supplied ur-type on this object. Optionally provide a data object to modify the created entity.`, spawn: `Create an instance of a supplied ur-type on this object. Optionally provide a data object to modify the created entity.`,

View file

@ -141,11 +141,33 @@ static void gameobject_setpickcolor(struct gameobject *go) {
*/ */
} }
static void velocityFn(cpBody *body, cpVect gravity, cpFloat damping, cpFloat dt)
{
struct gameobject *go = id2go(cpBodyGetUserData(body));
if (!go)
cpBodyUpdateVelocity(body,gravity,damping,dt);
// cpFloat d = isnan(go->damping) ? damping : d;
cpVect g = go->gravity ? gravity : cpvzero;
cpBodyUpdateVelocity(body,g,damping,dt);
if (!isinf(go->maxvelocity))
cpBodySetVelocity(body, cpvclamp(cpBodyGetVelocity(body), go->maxvelocity));
if (!isinf(go->maxangularvelocity)) {
float av = cpBodyGetAngularVelocity(body);
if (fabs(av) > go->maxangularvelocity)
cpBodySetAngularVelocity(body, copysignf(go->maxangularvelocity, av));
}
}
int MakeGameobject() { int MakeGameobject() {
struct gameobject go = { struct gameobject go = {
.scale = 1.f, .scale = 1.f,
.scale3 = (HMM_Vec3){1.f,1.f,1.f}, .scale3 = (HMM_Vec3){1.f,1.f,1.f},
.bodytype = CP_BODY_TYPE_STATIC, .bodytype = CP_BODY_TYPE_STATIC,
.maxvelocity = INFINITY,
.maxangularvelocity = INFINITY,
.mass = 1.f, .mass = 1.f,
.next = -1, .next = -1,
.sensor = 0, .sensor = 0,
@ -157,6 +179,7 @@ int MakeGameobject() {
go.cbs.separate.obj = JS_NULL; go.cbs.separate.obj = JS_NULL;
go.body = cpSpaceAddBody(space, cpBodyNew(go.mass, 1.f)); go.body = cpSpaceAddBody(space, cpBodyNew(go.mass, 1.f));
cpBodySetVelocityUpdateFunc(go.body, velocityFn);
int retid; int retid;

View file

@ -21,6 +21,10 @@ struct gameobject {
float mass; float mass;
float f; /* friction */ float f; /* friction */
float e; /* elasticity */ float e; /* elasticity */
float maxvelocity;
float maxangularvelocity;
int gravity;
float damping;
int flipx; /* 1 or -1 */ int flipx; /* 1 or -1 */
int flipy; int flipy;
int sensor; int sensor;

View file

@ -1131,7 +1131,36 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
case 150: case 150:
draw_drawmodel(js2ptr(argv[1])); draw_drawmodel(js2ptr(argv[1]));
break; break;
case 151:
js2go(argv[1])->maxvelocity = js2number(argv[2]);
break;
case 152:
ret = num2js(js2go(argv[1])->maxvelocity);
break;
case 153:
cpBodySetTorque(js2go(argv[1])->body, js2number(argv[2]));
break;
case 154:
js2go(argv[1])->maxangularvelocity = js2number(argv[2]);
break;
case 155:
ret = num2js(js2go(argv[1])->maxangularvelocity);
break;
case 156:
js2go(argv[1])->damping = js2number(argv[2]);
break;
case 157:
ret = num2js(js2go(argv[1])->damping);
break;
case 158:
js2go(argv[1])->gravity = js2bool(argv[2]);
break;
case 159:
ret = bool2js(js2go(argv[1])->gravity);
break;
} }
if (str) if (str)
@ -1375,6 +1404,9 @@ JSValue duk_set_body(JSContext *js, JSValueConst this, int argc, JSValueConst *a
case 13: case 13:
cpBodySetMoment(go->body, js2number(argv[2])); cpBodySetMoment(go->body, js2number(argv[2]));
return JS_NULL; return JS_NULL;
case 14:
cpBodyApplyForceAtLocalPoint(go->body, js2vec2(argv[2]), js2vec2(argv[3]));
return JS_NULL;
} }
cpSpaceReindexShapesForBody(space, go->body); cpSpaceReindexShapesForBody(space, go->body);

View file

@ -6,6 +6,8 @@
#include "jsffi.h" #include "jsffi.h"
#include "font.h" #include "font.h"
#include "gameobject.h"
#include "ftw.h" #include "ftw.h"
#include "stb_ds.h" #include "stb_ds.h"
@ -304,7 +306,9 @@ void call_nk_gui() { js_callee_exec(&nk_gui_callee, 0, NULL); }
static struct callee physupdate_callee; static struct callee physupdate_callee;
void register_physics(struct callee c) { physupdate_callee = c; } void register_physics(struct callee c) { physupdate_callee = c; }
void call_physics(double dt) { callee_dbl(physupdate_callee, dt); } void call_physics(double dt) {
callee_dbl(physupdate_callee, dt);
}
struct callee debug_callee; struct callee debug_callee;
void register_debug(struct callee c) { debug_callee = c; } void register_debug(struct callee c) { debug_callee = c; }