[SOLVED] Animation for Basic Ai not working

I have a problem :sweat_smile: I tried adding the anim script to the basic ai script and i’ve tried using other scripts to solve the problem. The problem is that the animation wont play, sometimes it will but if it does it makes this glitch, My goal is to try and make the Ai play a animation by different events like "Combat state, idle state and more.

project link - PlayCanvas | HTML5 Game Engine
Code link - PlayCanvas | HTML5 Game Engine

/* jshint esversion: 6 */
/* jshint asi: true */
/* jshint expr: true */
/* jslint vars: true */

var MaiAi = pc.createScript('MaiAi');

var MaiAiTargetIsAudible = false;

MaiAi.attributes.add('MaiAiTarget', { type: 'entity', title: 'Target Entity' });
MaiAi.attributes.add('MaiAiHeight', { type: 'number', default: 1, title: 'MaiAi Height' });
MaiAi.attributes.add('MaiAiSpeed', { type: 'number', default: 1, title: 'MaiAi Speed' });
MaiAi.attributes.add('MaiAiRange', { type: 'number', default: 5, title: 'MaiAi Range' });

// initialize code called once per entity
MaiAi.prototype.initialize = function () {
    this.MaiAiState = 'search';
    this.MaiAiTargetDistance = 0;
    this.MaiAiStateTimer = 0;
    this.MaiAiCanSeeTarget = false;
    this.MaiAiDestination = new pc.Vec3();
    this.MaiAiOrigin = new pc.Vec3();
    this.MaiAiOrigin.copy(this.entity.getPosition());
};

// update code called every frame
MaiAi.prototype.update = function (dt) {
    this.detectroot(dt);
    this.applyGravity(dt);
    this.updateState(dt);
    this.updateView(dt);
};

// this function controls the view of the MaiAi
MaiAi.prototype.updateView = function () {
    this.MaiAiCanSeeTarget = false;


    if (this.MaiAiTarget) {
        // check distance to target
        this.MaiAiTargetDistance = this.entity.getPosition().distance(this.MaiAiTarget.getPosition());

        // check if target is in range
        if (this.MaiAiTargetDistance < 50) {
            var target = this.MaiAiTarget.getPosition();
            target.sub(this.entity.getPosition()).normalize();
            var dot = target.dot(this.entity.forward);

            // check if target is in front
            if (dot > 0.5 || this.MaiAiCanSeeTarget) {
                var start = new pc.Vec3(this.entity.getPosition().x, this.entity.getPosition().y + this.MaiAiHeight / 2, this.entity.getPosition().z);
                var end = new pc.Vec3(this.MaiAiTarget.getPosition().x, this.MaiAiTarget.getPosition().y + this.MaiAiHeight / 2, this.MaiAiTarget.getPosition().z);
                var result = this.raycastRigidbody(start, end);

                // check if target is in view
                if (result && result.entity === this.MaiAiTarget) {
                    this.MaiAiCanSeeTarget = true;
                    //this.app.drawLine(start, end);

                    if (!this.MaiAiIsBlocked) {
                        this.MaiAiDestination.copy(new pc.Vec3(this.MaiAiTarget.getPosition().x, this.MaiAiHeight, this.MaiAiTarget.getPosition().z));
                    }
                }
            }
        }
    }
};

// this function controls the MaiAi state
MaiAi.prototype.updateState = function (dt) {
    // update current state
    if (this.MaiAiTarget && this.MaiAiCanSeeTarget) {
        this.MaiAiStateTimer = 0;

        if (this.MaiAiTargetDistance > 15) {
            console.log("alert");
        }

        else if (this.MaiAiTargetDistance > 10) {
            console.log("combat");
        }

        else if (this.MaiAiTargetDistance < 5) {
            console.log("Following");
        }

        else if (this.MaiAiState != 'combat') {
            console.log("combat");
        }
    }

    else if (this.MaiAiTarget && MaiAiTargetIsAudible && this.MaiAiTargetDistance < 15) {
        this.MaiAiStateTimer = 0;
        this.MaiAiState = 'alert';
    }

    else if (this.MaiAiState != 'search') {
        this.MaiAiStateTimer += dt;

        if (this.MaiAiStateTimer > 2) {
            this.MaiAiStateTimer = 0;
            this.MaiAiState = 'search';
        }
    }

    // exectue current state
    this[this.MaiAiState](dt);
};

// alert state
MaiAi.prototype.alert = function (dt) {
    this.MaiAiDestination.copy(new pc.Vec3(this.MaiAiTarget.getPosition().x, this.entity.getPosition().y, this.MaiAiTarget.getPosition().z));
    this.entity.lookAt(new pc.Vec3(this.MaiAiDestination.x, this.entity.getPosition().y, this.MaiAiDestination.z), this.
        this.entity.anim.baseLayer.play('Idle'))
}

// chase state
MaiAi.prototype.chase = function (dt) {
    this.MaiAiDestination.copy(new pc.Vec3(this.MaiAiTarget.getPosition().x, this.entity.getPosition().y, this.MaiAiTarget.getPosition().z));
    this.entity.lookAt(new pc.Vec3(this.MaiAiDestination.x, this.entity.getPosition().y, this.MaiAiDestination.z));

    if (this.MaiAiTargetDistance > 3) {
        this.entity.translateLocal(0, 0, -this.MaiAiSpeed * dt * 2);
        this.entity.anim.baseLayer.play('Running')
    }
}

// combat state
MaiAi.prototype.combat = function (dt) {
    this.MaiAiDestination.copy(new pc.Vec3(this.MaiAiTarget.getPosition().x, this.entity.getPosition().y, this.MaiAiTarget.getPosition().z));
    this.entity.lookAt(new pc.Vec3(this.MaiAiDestination.x, this.entity.getPosition().y, this.MaiAiDestination.z));
    this.entity.anim.baseLayer.play('Jump')
}

// search state
MaiAi.prototype.search = function (dt) {
    var distanceToDestination = this.entity.getPosition().distance(new pc.Vec3(this.MaiAiDestination.x, this.entity.getPosition().y, this.MaiAiDestination.z));
    if (!this.MaiAiIsBlocked && distanceToDestination > 0.2) {
        this.entity.lookAt(new pc.Vec3(this.MaiAiDestination.x, this.entity.getPosition().y, this.MaiAiDestination.z));
        this.entity.translateLocal(0, 0, -this.MaiAiSpeed * dt);
    }
    else {
        this.randomDestination();
    }
    this.entity.anim.baseLayer.play('Walk')
}

// get random point based on origin and range
MaiAi.prototype.randomDestination = function () {
    const xPosition = pc.math.random(this.MaiAiOrigin.x - this.MaiAiRange, this.MaiAiOrigin.x + this.MaiAiRange);
    const zPosition = pc.math.random(this.MaiAiOrigin.z - this.MaiAiRange, this.MaiAiOrigin.z + this.MaiAiRange);
    this.MaiAiDestination.copy(new pc.Vec3(xPosition, this.entity.getPosition().y + this.MaiAiHeight, zPosition));
};

// prevent the MaiAi is moving inside an root
MaiAi.prototype.detectroot = function () {
    var position = this.entity.getPosition();
    var start = new pc.Vec3(position.x, position.y + (this.MaiAiHeight / 2), position.z);
    var end = this.MaiAiDestination;
    var result = this.app.systems.rigidbody.raycastFirst(start, end);

    this.MaiAiIsBlocked = false;
    if (result && (result.entity.tags.has("root"))) {
        var distanceToroot = this.entity.getPosition().distance(result.point);

        if (distanceToroot < 2) {
            this.MaiAiIsBlocked = true;
            this.randomDestination();
        }
    }
};

// apply fake gravity to MaiAi
MaiAi.prototype.applyGravity = function () {
    var position = this.entity.getPosition();
    var start = new pc.Vec3(position.x, position.y + (this.MaiAiHeight / 2), position.z);
    var end = new pc.Vec3(position.x, position.y - this.MaiAiHeight, position.z);
    var result = this.app.systems.rigidbody.raycastFirst(start, end);

    if (result && result.entity != this.entity) {
        if (Math.abs(result.point.y - this.entity.getPosition().y) - this.MaiAiHeight < -0.2) {
            this.entity.translateLocal(0, 0.1, 0);
        }
    }

    else {
        this.entity.translateLocal(0, -0.1, 0);
    }
    this.entity.anim.baseLayer.play('Jump')
}

MaiAi.prototype.raycastRigidbody = function (start, end) {
    var distanceVec3 = new pc.Vec3();
    var closestDistanceSqr = Number.MAX_VALUE;
    var closestResult = null;
    var results = this.app.systems.rigidbody.raycastAll(start, end);

    for (var i = 0; i < results.length; ++i) {
        var result = results[i];

        if (result.entity.rigidbody) {
            distanceVec3.sub2(result.point, start);
            var distanceSqr = distanceVec3.lengthSq();
            if (distanceSqr < closestDistanceSqr) {
                closestDistanceSqr = distanceSqr;
                closestResult = result;
            }
        }
    }

    return closestResult;
};

btw this is my other account so i will reply on this page too

Hi @Kelvin_Riley and welcome! I guess the problem is in your Anim State Graph. Make sure you set the correct conditions for the transitions.

1 Like

The Anim State Graph is the same names for the call i gave it in the script

@Kelvin_Riley I forked your project and I do not see any animation transitions that have a duration set. I would at least set them all to .1 to start. Also, I think a parameter is missing in the state graph that is required to control each state change. These are the things that @Albertos I think is referring to.

1 Like

I’m not sure if transitions are required with this method. I will check the project when I’m home.

2 Likes

Do you think you could do that so i could fork that and see if it works because not going to lie i don’t know how to set up the transitions that good but i’ll try my best to go by what you said

@Kelvin_Riley2 I would stand by as I may have misunderstood the issue.

@Kelvin_Riley Did you use an example for your current setup?

I used the Anim State Graph Tutorial


The “this.entity.anim.baseLayer.activeState === ‘Punch’” part i used but at first it wouldn’t play so insteed i replaced the (“activeState === ‘Punch’”) with (.play(“Exam”). It trys to play the animation but it’s all glitchy

I checked your project and there are a couple of issues.

  1. With your method you have to remove all transitions, because your transitions have no conditions. Because of this all your animations are conflicting with each other.
  2. Your jump animation is started at the wrong place and is conflicting with your animations too. I suggest to skip this animation for now.
  3. You need to make sure your animation is only started once. Otherwise the animation will not played correctly, because it’s strated over and over.
if (this.entity.anim.baseLayer.activeState != 'Walk') {
    this.entity.anim.baseLayer.play('Walk');
}

The downside of this method is that there is no blending between animations. If this is no problem, you can apply above suggestions. Otherwise I would suggest to use transitions with conditions. You can use for example one integer condition for all transitions, to switch to the correct animation.

2 Likes

@Kelvin_Riley2 I briefly went through your animations. I think when you downloaded the animations from Mixamo you may have not downloaded them as walk or run in place. This being said, whichever method you use to move your character will glitch some since the character will walk several paces ahead and then get yanked back by the movement in the AI script. Here is a screen shot from Mixamo.

image

2 Likes

Thank you for the advice @Albertos but when i removed all transitions and added your recommend code


The same bug occurred :frowning:

I will try to keep all animation in place !! thx @Tirk182

No, I have checked this myself and it’s working correctly if you apply all changes.

Okay, can you make a example project so i can fork it and try it out because on my projects the error is still there again @albertos

I see you did not use my code example of how to prevent the animation is starting repeatedly.


When i try it , it comes out as a error