I'm still making progress, this time on behavior scripts. I have a lot to do to cover many situations.
I have so far made 30 scripts out of about 150. Here's an example of a script named Phantom.
All scripts are built on the same basis.
That is, a class that extends Behaviors
This parent class groups functions common to all behavior scripts and helps to perform them (the addConfig in the init() function is a good example)
a init()
function that is used to display behavior parameters in the editor.
a start()
function to initialize on the game side.
a trigger()
function that triggers the event (as the character enters a zone).
a execute()
function to execute the behavior.
/*jshint esversion: 6 */
/**
* @class Phantom
* @description Shows a ghost or a spectral entity with special effects.
* @extends {Behaviors}
*/
class Phantom extends Behaviors {
constructor(object) {
super(object);
this.mesh = null;
this.hasAppeared = false;
}
init() {
this.addConfig("phantomId", "input", {
label: "Phantom Object ID",
placeholder: "ghost01",
type: "text",
id: "phantomId",
dropAviable: "Mesh",
onChange: (v) => this.savePropertyValue("phantomId", v)
});
this.addConfig("appearanceMode", "input", {
label: "Appearance Mode",
type: "select",
options: [
{text: "Show-Fade/Disappear", value: "Show-Fade/Disappear"},
{text: "Show-Approach", value: "Show-Approach"},
{text: "Show-Face-Approach", value: "Show-Face-Approach"},
{text: "Show-Move-Fade/Disappear", value: "Show-Move-Fade/Disappear"}
],
defaultValue: "Show-Fade/Disappear",
id: "appearanceMode",
onChange: (v) => this.savePropertyValue("appearanceMode", v)
});
this.addConfig("alphaLevel", "input", {
label: "Transparency (0-1)",
type: "number",
min: 0,
max: 1,
step: 0.01,
defaultValue: 0.5,
id: "alphaLevel",
onChange: (v) => this.savePropertyValue("alphaLevel", v)
});
this.addConfig("glowColor", "input", {
label: "Emissive Color (Aura)",
type: "color",
defaultValue: "#ffffff",
id: "glowColor",
onChange: (v) => this.savePropertyValue("glowColor", v)
});
this.addConfig("appearanceSpeed", "input", {
label: "Appearance Speed (s)",
type: "number",
defaultValue: 2,
min: 0,
id: "appearanceSpeed",
onChange: (v) => this.savePropertyValue("appearanceSpeed", v)
});
this.addConfig("stayDuration", "input", {
label: "Time before disappear (s)",
type: "number",
defaultValue: 4,
min: 0,
id: "stayDuration",
onChange: (v) => this.savePropertyValue("stayDuration", v)
});
this.addConfig("disappearMode", "input", {
label: "Disappearance Mode",
type: "select",
options: [
{text: "FadeOut", value: "FadeOut"},
{text: "Instant", value: "Instant"}
],
defaultValue: "FadeOut",
id: "disappearMode",
onChange: (v) => this.savePropertyValue("disappearMode", v)
});
this.addConfig("moveSpeed", "input", {
label: "Move Speed",
type: "number",
defaultValue: 1,
min: 0,
id: "moveSpeed",
onChange: (v) => this.savePropertyValue("moveSpeed", v)
});
this.addConfig("idleAnim", "input", {
label: "Idle Animation Name",
type: "text",
placeholder: "idle",
id: "idleAnim",
onChange: (v) => this.savePropertyValue("idleAnim", v)
});
this.addConfig("moveAnim", "input", {
label: "Move Animation Name",
type: "text",
placeholder: "move",
id: "moveAnim",
onChange: (v) => this.savePropertyValue("moveAnim", v)
});
this.addConfig("moveX", "input", {
label: "Move Distance X",
type: "number",
defaultValue: 0,
id: "moveX",
onChange: (v) => this.savePropertyValue("moveX", v)
});
this.addConfig("moveZ", "input", {
label: "Move Distance Z",
type: "number",
defaultValue: 0,
id: "moveZ",
onChange: (v) => this.savePropertyValue("moveZ", v)
});
this.addConfig("appearanceSound", "input", {
label: "Sound on appearance",
type: "search",
placeholder: "root/phantomAppear.mp3",
id: "appearanceSound",
dropAviable: "Sound",
onChange: (v) => this.savePropertyValue("appearanceSound", v)
});
this.addConfig("moveSound", "input", {
label: "Sound while moving",
type: "search",
placeholder: "root/phantomMove.mp3",
id: "moveSound",
dropAviable: "Sound",
onChange: (v) => this.savePropertyValue("moveSound", v)
});
}
start() {
const id = this.getBehaviorProperty("phantomId");
this.mesh = this.scene.getMeshById(id);
if (!this.mesh) {
console.warn("Phantom: mesh with ID not found:", id);
return;
}
this.mesh.setEnabled(false); // invisible by défaut
}
trigger(event) {
if (this.hasAppeared || !this.mesh) return;
this.hasAppeared = true;
this.executeAppearance();
}
executeAppearance() {
const mode = this.getBehaviorProperty("appearanceMode", "Show-Fade/Disappear");
const alphaTarget = parseFloat(this.getBehaviorProperty("alphaLevel", 0.5));
const glow = this.getBehaviorProperty("glowColor", "#ffffff");
const duration = parseFloat(this.getBehaviorProperty("appearanceSpeed", 2));
const stay = parseFloat(this.getBehaviorProperty("stayDuration", 4));
const disappearMode = this.getBehaviorProperty("disappearMode", "FadeOut");
const soundFile = this.getBehaviorProperty("appearanceSound");
// Activation du mesh
this.mesh.setEnabled(true);
this.mesh.material.alpha = 0;
this.mesh.material.emissiveColor = BABYLON.Color3.FromHexString(glow);
// Apparition en fondu
this.scene.beginAnimation(this.mesh, 0, 0, false); // clear anim
BABYLON.Animation.CreateAndStartAnimation("fadeIn", this.mesh.material, "alpha", 30, duration * 30, 0, alphaTarget, 0);
// Apparition sonore
if (soundFile) {
this.game.soundEngine.addSpatialSound("zonePhantomSound", soundFile, this.mesh);
this.game.soundEngine.attachSoundToMesh("zonePhantomSound", this.mesh, true);
this.game.soundEngine.play("zonePhantomSound");
}
// Mouvement éventuel
if (mode.includes("Move")) {
this.movePhantom(stay);
}
// Disparition
setTimeout(() => {
if (disappearMode === "FadeOut") {
BABYLON.Animation.CreateAndStartAnimation("fadeOut", this.mesh.material, "alpha", 30, 30, alphaTarget, 0, 0, () => {
this.mesh.setEnabled(false);
});
} else {
this.mesh.setEnabled(false);
}
}, (duration + stay) * 1000);
}
movePhantom(stay) {
const dx = parseFloat(this.getBehaviorProperty("moveX", 0));
const dz = parseFloat(this.getBehaviorProperty("moveZ", 0));
const speed = parseFloat(this.getBehaviorProperty("moveSpeed", 1));
const moveSound = this.getBehaviorProperty("moveSound");
const from = this.mesh.position.clone();
const to = from.add(new BABYLON.Vector3(dx, 0, dz));
BABYLON.Animation.CreateAndStartAnimation("moveGhost", this.mesh, "position", 30, speed * 30, from, to, 0, null);
if (moveSound) {
this.game.soundEngine.addSpatialSound("zonePhantomMoveSound", moveSound, this.mesh);
this.game.soundEngine.attachSoundToMesh("zonePhantomMoveSound", this.mesh, true);
this.game.soundEngine.play("zonePhantomMoveSound");
}
}
}
BEHAVIOR.register("Phantom", Phantom);