Tělesa

Bodies Overview

JoltBody je bezpečný high-level GML zástupce jednoho rigid body ve výchozím Jolt worldu.

Tato stránka shrnuje společný lifecycle, motion typy, options, vlastnosti, kolize, raycasty a ověřené limity všech tvarů v sekci Bodies.

JoltBody#

JoltBody obsahuje generační native handle, world handle, typ shape, motion type, metadata rozměrů, GML user_data, volitelného vlastníka GameMaker instance a kolizní události posledního aktualizovaného framu. Není to C++ pointer. Handle kóduje slot a generaci; po zničení se generace změní a zastaralý handle je odmítnut. Hodnota 0 je neplatná. V běžném kódu používejte JoltBody, nikoli číslo z body.get_native_handle().

Motion typy#

Motion typeKonstantaChování
DynamicJOLT_MOTION_DYNAMICSimulované těleso reagující na gravitaci, kontakty, síly, impulzy a constraints. Vytváří se funkcemi s příponou _create_dynamic.
StaticJOLT_MOTION_STATICNepohyblivá kolizní geometrie pro podlahy, stěny, level mesh a terrain; neintegruje velocity a není určena pro síly.
KinematicJOLT_MOTION_KINEMATICTěleso řízené aplikací pomocí move_kinematic nebo move_kinematic_euler; může fyzicky ovlivňovat Dynamic body.

Motion type lze číst body.get_motion_type() a měnit body.set_motion_type(type). Mesh, HeightField a Plane podporují v high-level API pouze Static.

Souřadnice a jednotky#

  • API je pravotočivé a Z-up: X vlevo/vpravo, Y dopředu/dozadu, Z nahoru/dolů.
  • Native převod rigid-body souřadnic je identitní.
  • Doporučená konvence je 1 jednotka = 1 metr; wrapper žádnou fyzikální jednotku nevynucuje.
  • High-level Euler úhly jsou ve stupních; quaternion má tvar {x, y, z, w}.
  • High-level primitiva přijímají celé rozměry, low-level API výslovně half extents nebo half height.

Lifecycle#

GML
jolt_start();
var body = jolt_box_create_dynamic(0, 0, 5, 1, 1, 1);

// Step event
jolt_update();

// Clean Up event
body.destroy();
jolt_stop();

jolt_start(options) vytvoří výchozí world a lze jej volat opakovaně. jolt_update(frame_seconds) používá fixed timestep, standardně 1/60 s, provede simulaci a zpřístupní kontakty. jolt_stop() je idempotentní a odstraní spravované bodies, constraints, shape resources, compound buildery i world.

body.destroy() vrací true i při opakovaném volání. Poté body.is_valid() vrací false, GML handle se vynuluje, stale native handle je odmítnut a související high-level constraints jsou zneplatněny.

Společný options struct#

PoložkaVýchozí hodnotaPopis
rotation{0,0,0,1}Quaternion {x,y,z,w}; konečný a nenulový, Native jej normalizuje.
rotation_euler{0,0,0}Euler úhly ve stupních. Je-li zadána i rotation, rotation_euler se aplikuje později; doporučeno je zadat pouze jednu.
velocity{0,0,0}Počáteční world-space lineární rychlost; není povolena pro Static.
angular_velocity{0,0,0}Počáteční úhlová rychlost v radiánech za sekundu; není povolena pro Static.
friction0.2Konečné číslo >= 0; uvedený default platí pro Jolt 5.6.0.
restitution0.0Konečné číslo v rozsahu 0..1.
gravity_factor1.0Konečný násobek world gravity; vyžaduje motion properties.
linear_damping0.05Konečné číslo >= 0; vyžaduje motion properties.
angular_damping0.05Konečné číslo >= 0; vyžaduje motion properties.
massJolt mass propertiesKonečné číslo > 0, pouze Dynamic.
allow_sleepingtrueBoolean; vyžaduje motion properties.
layerjolt_default_layer()Platný JoltLayer ze stejného worldu.
sensorfalseBoolean určující trigger/sensor režim.
impact_sensorfalseBoolean nebo {min_value:real >= 0}; ukládá poslední relevantní impact, min_value je impulse v N·s.
ccdfalseBoolean; true zapne Jolt LinearCast CCD pro Dynamic rigid body.
motion_qualityDiscreteAdvanced konstanta Discrete nebo LinearCast; má před ccd přednost.
center_of_mass_offset{0,0,0}Konečný lokální offset {x,y,z} mezi body originem a těžištěm.

Chyby a návratové hodnoty#

GML
if (is_undefined(body)) {
    var error = jolt_get_error();
    show_debug_message(error.context + ": " + error.message);
}

jolt_get_error() vrací {code, message, context}. K dispozici jsou také jolt_has_error(), jolt_get_error_message(), jolt_clear_error(), a pro low-level/native stav jolt_get_last_error_code() a jolt_get_last_error_message().

JoltBody metody#

Lifecycle a identita#

GML
body.is_valid()
body.get_native_handle()
body.get_world_handle()
body.destroy()

Transformace a pohyb#

GML
body.get_position()
body.set_position(x, y, z)
body.get_rotation()
body.get_rotation_euler()
body.set_rotation(x, y, z)
body.set_rotation(x, y, z, w)
body.set_rotation_euler(x, y, z)
body.get_transform()
body.get_velocity() / body.set_velocity(x, y, z)
body.get_angular_velocity() / body.set_angular_velocity(x, y, z)
body.move_kinematic(...)
body.move_kinematic_euler(...)

Síly a impulzy — pouze Dynamic#

GML
body.add_force(x, y, z)
body.add_force_at_position(fx, fy, fz, px, py, pz)
body.add_force_local(fx, fy, fz, local_x, local_y, local_z)
body.add_impulse(x, y, z)
body.add_impulse_at_position(ix, iy, iz, px, py, pz)
body.add_impulse_local(ix, iy, iz, local_x, local_y, local_z)
body.add_torque(x, y, z)
body.add_angular_impulse(x, y, z)

Varianty at_position používají world-space bod.

Vlastnosti a aktivace#

GML
body.set_friction(v) / body.get_friction()
body.set_restitution(v) / body.get_restitution()
body.set_gravity_factor(v) / body.get_gravity_factor()
body.set_linear_damping(v) / body.get_linear_damping()
body.set_angular_damping(v) / body.get_angular_damping()
body.set_mass(v) / body.get_mass()
body.activate() / body.deactivate() / body.is_active()
body.set_allow_sleeping(v) / body.get_allow_sleeping()
body.set_ccd(enabled) / body.get_ccd()
body.set_motion_quality(quality) / body.get_motion_quality()

Impact Sensor#

GML
var box = jolt_box_create_dynamic(0, 0, 5, 1, 1, 1, {
    impact_sensor: {min_value: 10.0}
});
var impact = box.get_impact_sensor();

box.set_impact_sensor(true, 10.0);

Impact Sensor je per-body opt-in snapshot posledního relevantního OnContactAdded nárazu. value a impulse jsou součet odhadovaných normálových contact impulses z Jolt EstimateCollisionResponse v N·s. Persistentní sequence umožňuje zpracovat každý nový impact právě jednou. Resting a sliding OnContactPersisted kontakty snapshot nepřepisují.

Výsledek obsahuje hit, value, impulse, normal_speed, position, normal, other_handle, živé other_body/other_user_data nebo undefined a sequence. Před prvním impactem je hit=false, sequence=0 a numerické hodnoty jsou nulové. Pokud vznikne více impactů v jednom native fyzikálním kroku, uloží se nejsilnější. Jedno jolt_update() může obsahovat několik kroků. Úplné chování a příklad jednorázové reakce najdete v Kolizní systém → Impact Sensor.

Continuous Collision Detection (CCD)#

Výchozí Discrete režim kontroluje kontakty v jednotlivých physics krocích, takže malé rychlé body může přeskočit tenkou překážku. LinearCast používá nativní Jolt shape cast podél lineárního pohybu automaticky během jolt_update().

GML
var bullet = jolt_sphere_create_dynamic(0, 0, 1, 0.05, {
    mass: 0.02,
    ccd: true
});
bullet.set_velocity(0, 100, 0);

bullet.set_ccd(false); // Discrete
bullet.set_ccd(true);  // LinearCast

Konstanty jsou JOLT_MOTION_QUALITY_DISCRETE a JOLT_MOTION_QUALITY_LINEAR_CAST. Runtime změna zachová activation, sleeping permission a obě rychlosti. Wrapper CCD povoluje pouze Dynamic non-sensor rigid body s kladným inner radius; současné Box, Sphere, Capsule, Cylinder, Tapered Capsule, Tapered Cylinder, Convex a Compound jsou kompatibilní.

CCD není jolt_raycast(), jolt_raycast_all() ani Shape Query. Uživatel nevolá ruční cast; jde o solver vlastnost JoltBody.

Těžiště#

GML
body.get_center_of_mass_position()
body.get_center_of_mass_offset()

center_of_mass_offset se volí při vytvoření a později jej nelze změnit. První metoda vrací aktuální world-space pozici těžiště, druhá lokální offset uložený high-level wrapperem. Hodí se, když se má vizuální pivot a bod vyvážení tělesa lišit.

Collision layers#

jolt_default_layer() vrací výchozí layer. jolt_layer_create(name) vytvoří další JoltLayer; world podporuje nejvýše 32 uživatelských vrstev včetně Default. jolt_layer_set_collision(layer_a, layer_b, enabled) symetricky nastaví kolizi dvojice a nové vrstvy spolu standardně kolidují.

GML
var trigger_layer = jolt_layer_create("Triggers");
jolt_layer_set_collision(trigger_layer, jolt_default_layer(), true);
var body = jolt_box_create_dynamic(0, 0, 3, 1, 1, 1, {layer: trigger_layer});

Layer lze zadat v options nebo později přes body.set_layer(). body.get_layer() vrací JoltLayer, případně undefined při chybě nebo neznámém handle.

Sensors#

Každý rigid shape lze označit {sensor:true} nebo body.set_sensor(true); body.is_sensor() stav čte. Sensor vytváří kontaktní callbacky bez fyzické collision response, funguje tedy jako trigger volume a může jej zasáhnout raycast.

user_data a GameMaker instance#

GML
body.set_user_data({kind:"Door", id:17});
var data = body.get_user_data();

user_data je čistě high-level GML hodnota. body.bind_instance(instance, use_as_user_data=true) sváže existující instanci a standardně ji uloží i jako user_data. body.sync() explicitně zapíše x, y, z a jolt_rotation_x/y/z/w; synchronizace není automatická. body.unbind_instance() vazbu odstraní. jolt_box_create_dynamic_for_instance(instance, width, depth, height, options) vytvoří a rovnou sváže Dynamic Box.

Kontakty#

Po jolt_update() vrací jolt_get_collisions() world eventy s poli type, body_a/body_b, handly a validitou obou bodies, position, normal, penetration_depth, sensor příznaky a user_data_a/user_data_b. Typ je JOLT_COLLISION_ENTER, STAY nebo EXIT.

body.get_collisions() vrací kontakty daného body s poli type, body, other_body, entity, other_entity, other_handle, other_valid, position, normal, penetration_depth a sensor. jolt_get_dropped_collision_count() hlásí eventy zahozené při přeplnění native fronty.

Raycasty#

GML
var hit = jolt_raycast(x1, y1, z1, x2, y2, z2, filter);
var hits = jolt_raycast_all(x1, y1, z1, x2, y2, z2, filter);

filter může být JoltLayer, pole vrstev, číselná maska nebo struct {layers:[...], ignore:body}, případně {mask:value, ignore:body}. Bez filtru se testují všechny vrstvy. Hit obsahuje hit, body, body_handle, entity, position, normal, fraction, distance, error_code a error_message. jolt_raycast_all() vrací seřazené pole. Mesh je jednostranný a výchozí raycast ignoruje zadní strany.

Debug Draw#

GML
jolt_debug_enable(true);
jolt_debug_set(JOLT_DEBUG_SHAPES, true);
jolt_debug_set(JOLT_DEBUG_SENSORS, true);
jolt_debug_draw();

JOLT_DEBUG_SHAPES kreslí nesenzorová body a sensory vyžadují JOLT_DEBUG_SENSORS. Motion a sleep stavy mají odlišné barvy. Jde o high-level vizualizaci z metadata, nikoli export interního Jolt shape; doporučené místo je poslední 3D pass, například Draw End.

Ověřené limity implementace#

  • Native body handly jsou 32bitové generační hodnoty, nikoli pointery.
  • Convex přijímá nejvýše 4096 bodů; výsledný hull nejvýše 256 vrcholů.
  • Mesh: 65536 vertices a 131072 triangles.
  • HeightField: čtvercová mřížka 4..512 samples.
  • Compound: nejvýše 64 children.
  • .jcol buffer v GML vrstvě: nejvýše 8 MiB.
  • Shape cache key: explicitní ASCII řetězec do 255 znaků; velká pole se automaticky nehashují.

Native smoke test pokrývá Dynamic Convex, Static Mesh, Static HeightField, raycasty, Z-up normals, kontakty, layers, sensors, invalid coplanar Convex, odmítnutí Dynamic Mesh, sanitizaci mesh, reusable shapes a stale handly.

Ověřené kontrakty operací#

Následující kontrakty rozlišují jednotky, návratové hodnoty, chyby a životnost podle aktuální implementace. Nevynechávej kontrolu výsledku: false, 0 a undefined mohou mít u různých metod odlišný význam.

Symbol / vlastníkSignaturaKontrakt operace
JoltBody.get_closest_pointJoltBody.get_closest_point(x, y, z)Světové XYZ m, dotaz jen proti tomuto tělesu; stejný hit/miss snapshot jako jolt_closest_point. Při chybě hit:false s error_code, nikoliv undefined.
JoltBody.get_transformJoltBody.get_transform()Světový {x, y, z} m / quaternion XYZW / Euler XYZ° / {position, rotation}, dle metody. undefined při chybě. Pozice je origin tělesa, ne COM.
JoltBody.get_world_handleJoltBody.get_world_handle()Uložený body/world handle pokud valid, jinak 0. Nepřenášej přes destroy či restart světa.
JoltBody.set_bottom_radiusJoltBody.set_bottom_radius(value)Boolean; kladný konečný rozměr v m pro odpovídající typ tvaru. Nevhodný typ selže; set_radius na tapered nastaví oba poloměry. Ostatní rozměry se zachovají.
JoltBody.set_depthJoltBody.set_depth(value)Boolean; kladný konečný rozměr v m pro odpovídající typ tvaru. Nevhodný typ selže; set_radius na tapered nastaví oba poloměry. Ostatní rozměry se zachovají.
JoltBody.set_half_extentJoltBody.set_half_extent(value)Boolean; pouze plane, nenulová konečná normála / kladný half extent v m; ostatní parametr zachován.
JoltBody.set_heightJoltBody.set_height(value)Boolean; kladný konečný rozměr v m pro odpovídající typ tvaru. Nevhodný typ selže; set_radius na tapered nastaví oba poloměry. Ostatní rozměry se zachovají.
JoltBody.set_heightfieldJoltBody.set_heightfield(heights,width,height,cell_size,cell_size_y=undefined)Boolean; nahrazuje geometrii pouze convex / mesh / heightfield podle názvu, se stejnou validací jako jolt_shape_* factory. Identita tělesa zůstává.
JoltBody.set_meshJoltBody.set_mesh(vertices,indices)Boolean; nahrazuje geometrii pouze convex / mesh / heightfield podle názvu, se stejnou validací jako jolt_shape_* factory. Identita tělesa zůstává.
JoltBody.set_normalJoltBody.set_normal(x,y,z)Boolean; pouze plane, nenulová konečná normála / kladný half extent v m; ostatní parametr zachován.
JoltBody.set_pointsJoltBody.set_points(points)Boolean; nahrazuje geometrii pouze convex / mesh / heightfield podle názvu, se stejnou validací jako jolt_shape_* factory. Identita tělesa zůstává.
JoltBody.set_radiusJoltBody.set_radius(value)Boolean; kladný konečný rozměr v m pro odpovídající typ tvaru. Nevhodný typ selže; set_radius na tapered nastaví oba poloměry. Ostatní rozměry se zachovají.
JoltBody.set_shapeJoltBody.set_shape(resource)Boolean; živý JoltShape/JoltCompound stejného světa, zachová identitu a gameplay nastavení tělesa i COM offset. Static-only geometrie nesmí vytvořit Dynamic/Kinematic. Aktualizuje metadata a debug cache.
JoltBody.set_sizeJoltBody.set_size(a,b,c=undefined)Boolean; box plné width/depth/height, capsule/cylinder height/radius, tapered height/top/bottom radius, vše m. Jiné tvary vyžadují jejich specifický setter.
JoltBody.set_top_radiusJoltBody.set_top_radius(value)Boolean; kladný konečný rozměr v m pro odpovídající typ tvaru. Nevhodný typ selže; set_radius na tapered nastaví oba poloměry. Ostatní rozměry se zachovají.
JoltBody.set_widthJoltBody.set_width(value)Boolean; kladný konečný rozměr v m pro odpovídající typ tvaru. Nevhodný typ selže; set_radius na tapered nastaví oba poloměry. Ostatní rozměry se zachovají.