mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
456 lines
14 KiB
JavaScript
456 lines
14 KiB
JavaScript
|
|
/**
|
|
GDevelop - Skeleton Object Extension
|
|
Copyright (c) 2017-2018 Franco Maciel (francomaciel10@gmail.com)
|
|
This project is released under the MIT License.
|
|
*/
|
|
|
|
|
|
/**
|
|
* The SkeletonRuntimeObject imports and displays skeletal animations files.
|
|
*
|
|
* @memberof gdjs
|
|
* @class SkeletonRuntimeObject
|
|
* @extends RuntimeObject
|
|
*/
|
|
gdjs.SkeletonRuntimeObject = function(runtimeScene, objectData){
|
|
gdjs.RuntimeObject.call(this, runtimeScene, objectData);
|
|
|
|
this.rootArmature = new gdjs.sk.Armature(this);
|
|
this.rootArmature.getRenderer().putInScene(this, runtimeScene);
|
|
this.rootArmature.setAsRoot();
|
|
this.animationPlaying = true;
|
|
this.animationSmooth = true;
|
|
this.timeScale = 1.0;
|
|
this.scaleX = 1.0;
|
|
this.scaleY = 1.0;
|
|
this.hitboxSlot = null;
|
|
|
|
this.manager = gdjs.SkeletonObjectsManager.getManager(runtimeScene);
|
|
this.getSkeletonData(runtimeScene, objectData);
|
|
|
|
// *ALWAYS* call `this.onCreated()` at the very end of your object constructor.
|
|
this.onCreated();
|
|
};
|
|
gdjs.SkeletonRuntimeObject.prototype = Object.create(gdjs.RuntimeObject.prototype);
|
|
gdjs.registerObject("SkeletonObject::Skeleton", gdjs.SkeletonRuntimeObject);
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.extraInitializationFromInitialInstance = function(initialInstanceData) {
|
|
if(initialInstanceData.customSize){
|
|
this.setWidth(initialInstanceData.width);
|
|
this.setHeight(initialInstanceData.height);
|
|
}
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.getSkeletonData = function(runtimeScene, objectData){
|
|
var skeletonData = this.manager.getSkeleton(runtimeScene, this.name, objectData);
|
|
|
|
if(skeletonData.armatures.length > 0){
|
|
this.rootArmature.loadData(skeletonData.armatures[skeletonData.rootArmature],
|
|
skeletonData,
|
|
objectData.debugPolygons);
|
|
|
|
this.rootArmature.resetState();
|
|
}
|
|
};
|
|
|
|
// RuntimeObject overwrites
|
|
gdjs.SkeletonRuntimeObject.prototype.setX = function(x){
|
|
gdjs.RuntimeObject.prototype.setX.call(this, x);
|
|
this.rootArmature.setX(x);
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.setY = function(y){
|
|
gdjs.RuntimeObject.prototype.setY.call(this, y);
|
|
this.rootArmature.setY(y);
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.setAngle = function(angle){
|
|
this.angle = angle;
|
|
this.rootArmature.setRot(angle);
|
|
gdjs.RuntimeObject.prototype.setAngle.call(this, angle);
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.getRendererObject = function(){
|
|
return this.rootArmature.getRendererObject();
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.getHitBoxes = function(){
|
|
if(this.hitboxSlot){
|
|
return this.hitboxSlot.getPolygons();
|
|
}
|
|
return [this.rootArmature.getAABB()];
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.stepBehaviorsPreEvents = function(runtimeScene){
|
|
var delta = this.getElapsedTime(runtimeScene) / 1000.0;
|
|
if(this.animationPlaying){
|
|
this.rootArmature.updateAnimation(delta*this.timeScale);
|
|
}
|
|
|
|
gdjs.RuntimeObject.prototype.stepBehaviorsPreEvents.call(this, runtimeScene);
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.update = function(runtimeScene){
|
|
this.rootArmature.update();
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.getDrawableX = function(){
|
|
return this.getX() - this.rootArmature.shared.aabb[0][0] * Math.abs(this.scaleX);
|
|
};
|
|
gdjs.SkeletonRuntimeObject.prototype.getDrawableY = function(){
|
|
return this.getY() - this.rootArmature.shared.aabb[0][1] * Math.abs(this.scaleY);
|
|
};
|
|
|
|
// Object instructions
|
|
gdjs.SkeletonRuntimeObject.prototype.getScaleX = function(){
|
|
return this.scaleX;
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.setScaleX = function(scaleX){
|
|
this.scaleX = scaleX;
|
|
this.rootArmature.setSx(scaleX);
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.getScaleY = function(){
|
|
return this.scaleY;
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.setScaleY = function(scaleY){
|
|
this.scaleY = scaleY;
|
|
this.rootArmature.setSy(scaleY);
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.getWidth = function(){
|
|
return this.rootArmature.getDefaultWidth() * Math.abs(this.scaleX);
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.setWidth = function(width){
|
|
if(width < 0) width = 0;
|
|
this.setScaleX(width / this.rootArmature.getDefaultWidth());
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.getHeight = function(){
|
|
return this.rootArmature.getDefaultHeight() * Math.abs(this.scaleY);
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.setHeight = function(height){
|
|
if(height < 0) height = 0;
|
|
this.setScaleY(height / this.rootArmature.getDefaultHeight());
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.setDefaultHitbox = function(slotPath){
|
|
if(slotPath === ""){
|
|
this.hitboxSlot = null;
|
|
return;
|
|
}
|
|
var slot = this.getSlot(slotPath);
|
|
if(slot){
|
|
this.hitboxSlot = slot;
|
|
}
|
|
};
|
|
|
|
// Animation instructions
|
|
gdjs.SkeletonRuntimeObject.prototype.isAnimationPaused = function(){
|
|
return !this.animationPlaying;
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.setAnimationPaused = function(paused){
|
|
this.animationPlaying = !paused;
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.isAnimationFinished = function(){
|
|
return this.rootArmature.isAnimationFinished();
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.getAnimationTime = function(){
|
|
return this.rootArmature.getAnimationTime();
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.setAnimationTime = function(time){
|
|
this.rootArmature.setAnimationTime(time);
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.getAnimationTimeLength = function(){
|
|
return this.rootArmature.getAnimationTimeLength();
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.getAnimationFrame = function(){
|
|
return this.rootArmature.getAnimationFrame();
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.setAnimationFrame = function(frame){
|
|
this.rootArmature.setAnimationFrame(frame);
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.getAnimationFrameLength = function(){
|
|
return this.rootArmature.getAnimationFrameLength();
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.getAnimationIndex = function(){
|
|
return this.rootArmature.getAnimationIndex();
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.setAnimationIndex = function(newAnimation, blendTime=0, loops=-1){
|
|
this.rootArmature.setAnimationIndex(newAnimation, blendTime, loops);
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.getAnimationName = function(){
|
|
return this.rootArmature.getAnimationName();
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.setAnimationName = function(newAnimation, blendTime=0, loops=-1){
|
|
this.rootArmature.setAnimationName(newAnimation, blendTime, loops);
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.isAnimationSmooth = function(){
|
|
return this.animationSmooth;
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.setAnimationSmooth = function(smooth){
|
|
this.animationSmooth = smooth;
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.getAnimationTimeScale = function(){
|
|
return this.timeScale;
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.setAnimationTimeScale = function(timeScale){
|
|
if(timeScale < 0) timeScale = 0; // Support negative timeScale (backward animation) ?
|
|
this.timeScale = timeScale;
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.resetAnimation = function(){
|
|
this.rootArmature.resetAnimation();
|
|
};
|
|
|
|
// Bone instructions
|
|
gdjs.SkeletonRuntimeObject.prototype.getBoneX = function(bonePath){
|
|
var bone = this.getBone(bonePath);
|
|
return bone ? bone.getGlobalX() : 0;
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.setBoneX = function(bonePath, x){
|
|
var bone = this.getBone(bonePath);
|
|
if(bone){
|
|
bone.setGlobalX(x);
|
|
bone.update();
|
|
}
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.getBoneY = function(bonePath){
|
|
var bone = this.getBone(bonePath);
|
|
return bone ? bone.getGlobalY() : 0;
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.setBoneY = function(bonePath, y){
|
|
var bone = this.getBone(bonePath);
|
|
if(bone){
|
|
bone.setGlobalY(y);
|
|
bone.update();
|
|
}
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.getBoneAngle = function(bonePath){
|
|
var bone = this.getBone(bonePath);
|
|
return bone ? bone.getGlobalRot() : 0;
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.setBoneAngle = function(bonePath, angle){
|
|
var bone = this.getBone(bonePath);
|
|
if(bone){
|
|
bone.setGlobalRot(angle);
|
|
bone.update();
|
|
}
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.getBoneScaleX = function(bonePath){
|
|
var bone = this.getBone(bonePath);
|
|
return bone ? bone.getSx() : 0;
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.setBoneScaleX = function(bonePath, scaleX){
|
|
var bone = this.getBone(bonePath);
|
|
if(bone){
|
|
bone.setSx(scaleX);
|
|
bone.update();
|
|
}
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.getBoneScaleY = function(bonePath){
|
|
var bone = this.getBone(bonePath);
|
|
return bone ? bone.getSy() : 0;
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.setBoneScaleY = function(bonePath, scaleY){
|
|
var bone = this.getBone(bonePath);
|
|
if(bone){
|
|
bone.setSy(scaleY);
|
|
bone.update();
|
|
}
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.resetBone = function(bonePath){
|
|
var bone = this.getBone(bonePath);
|
|
if(bone){
|
|
bone.resetState();
|
|
}
|
|
};
|
|
|
|
// Slot instructions
|
|
gdjs.SkeletonRuntimeObject.prototype.setSlotColor = function(slotPath, color){
|
|
var slot = this.getSlot(slotPath);
|
|
if(slot){
|
|
var rgb = color.split(";");
|
|
if(rgb.length < 3) return;
|
|
slot.setColor(...rgb);
|
|
}
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.isSlotVisible = function(slotPath){
|
|
var slot = this.getSlot(slotPath);
|
|
return slot ? slot.getVisible() : false;
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.setSlotVisible = function(slotPath, visible){
|
|
var slot = this.getSlot(slotPath);
|
|
if(slot){
|
|
slot.setVisible(visible);
|
|
}
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.getSlotZOrder = function(slotPath){
|
|
var slot = this.getSlot(slotPath);
|
|
return slot ? slot.getZ() : 0;
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.setSlotZOrder = function(slotPath, z){
|
|
var slot = this.getSlot(slotPath);
|
|
if(slot){
|
|
slot.setZ(z);
|
|
}
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.isPointInsideSlot = function(slotPath, x, y){
|
|
var hitBoxes = this.getPolygons(slotPath);
|
|
if(!hitBoxes) return false;
|
|
|
|
for(var i = 0; i < this.hitBoxes.length; ++i) {
|
|
if ( gdjs.Polygon.isPointInside(hitBoxes[i], x, y) )
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
// Extension instructions
|
|
gdjs.SkeletonRuntimeObject.prototype.raycastSlot = function(slotPath, x, y, angle, dist, closest){
|
|
var result = gdjs.Polygon.raycastTest._statics.result;
|
|
result.collision = false;
|
|
|
|
var endX = x + dist*Math.cos(angle*Math.PI/180.0);
|
|
var endY = y + dist*Math.sin(angle*Math.PI/180.0);
|
|
var testSqDist = closest ? dist*dist : 0;
|
|
|
|
var hitBoxes = this.getPolygons(slotPath);
|
|
if(!hitBoxes) return result;
|
|
|
|
for(var i=0; i<hitBoxes.length; i++){
|
|
var res = gdjs.Polygon.raycastTest(hitBoxes[i], x, y, endX, endY);
|
|
if ( res.collision ) {
|
|
if ( closest && (res.closeSqDist < testSqDist) ) {
|
|
testSqDist = res.closeSqDist;
|
|
result = res;
|
|
}
|
|
else if ( !closest && (res.farSqDist > testSqDist) && (res.farSqDist <= dist*dist) ) {
|
|
testSqDist = res.farSqDist;
|
|
result = res;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
};
|
|
|
|
// Warning!, assuming gdjs.evtTools.object.twoListsTest calls the predicate
|
|
// respecting the given objects lists paramenters order
|
|
gdjs.SkeletonRuntimeObject.slotObjectCollisionTest = function(skl, obj, slotPath){
|
|
var hitBoxes1 = skl.getPolygons(slotPath);
|
|
if(!hitBoxes1) return false;
|
|
|
|
var hitBoxes2 = obj.getHitBoxes();
|
|
for(var k=0, lenBoxes1=hitBoxes1.length; k<lenBoxes1; ++k){
|
|
for(var l=0, lenBoxes2=hitBoxes2.length; l<lenBoxes2; ++l){
|
|
if (gdjs.Polygon.collisionTest(hitBoxes1[k], hitBoxes2[l], false).collision){
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.slotSlotCollisionTest = function(skl1, skl2, slotPaths){
|
|
var hitBoxes1 = skl1.getPolygons(slotPaths[0]);
|
|
var hitBoxes2 = skl2.getPolygons(slotPaths[1]);
|
|
if(!hitBoxes1 || !hitBoxes2) return false;
|
|
|
|
for(var k=0, lenBoxes1=hitBoxes1.length; k<lenBoxes1; ++k){
|
|
for(var l=0, lenBoxes2=hitBoxes2.length; l<lenBoxes2; ++l){
|
|
if (gdjs.Polygon.collisionTest(hitBoxes1[k], hitBoxes2[l], false).collision){
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
// Helpers
|
|
gdjs.SkeletonRuntimeObject.prototype.getSlot = function(slotPath){
|
|
var slotArray = slotPath.split("/");
|
|
var currentSlot = null;
|
|
if(slotArray[0] in this.rootArmature.slotsMap){
|
|
currentSlot = this.rootArmature.slotsMap[slotArray[0]];
|
|
for(var i=1; i<slotArray.length; i++){
|
|
if(currentSlot.type === gdjs.sk.SLOT_ARMATURE &&
|
|
slotArray[i] in currentSlot.childArmature.slotsMap){
|
|
currentSlot = currentSlot.childArmature.slotsMap[slotArray[i]];
|
|
}
|
|
else{
|
|
return null
|
|
}
|
|
}
|
|
}
|
|
return currentSlot;
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.getBone = function(bonePath){
|
|
var slotArray = bonePath.split("/");
|
|
var boneName = slotArray.pop();
|
|
|
|
if(slotArray.length === 0 && boneName in this.rootArmature.bonesMap){
|
|
return this.rootArmature.bonesMap[boneName];
|
|
}
|
|
|
|
var slot = this.getSlot(slotArray.join("/"));
|
|
if(slot && slot.type === gdjs.sk.SLOT_ARMATURE && boneName in slot.childArmature.bonesMap){
|
|
return slot.childArmature.bonesMap[boneName];
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
gdjs.SkeletonRuntimeObject.prototype.getPolygons = function(slotPath){
|
|
if(slotPath === ""){
|
|
return this.rootArmature.getHitBoxes();
|
|
}
|
|
|
|
var slot = this.getSlot(slotPath);
|
|
if(slot){
|
|
return slot.getPolygons();
|
|
}
|
|
|
|
return null;
|
|
};
|