⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 aibot.java

📁 Developing Games in Java 源代码
💻 JAVA
字号:
package com.brackeen.javagamebook.ai;

import com.brackeen.javagamebook.ai.pattern.AimPattern;
import com.brackeen.javagamebook.path.PathFinder;
import com.brackeen.javagamebook.path.PathBot;
import com.brackeen.javagamebook.math3D.*;
import com.brackeen.javagamebook.game.*;
import com.brackeen.javagamebook.util.MoreMath;
import com.brackeen.javagamebook.shooter3D.MessageQueue;

public class AIBot extends PathBot {

    public static final int NORMAL_STATE_IDLE = 0;
    public static final int NORMAL_STATE_PATROL = 1;
    public static final int NORMAL_STATE_CHASE = 2;
    public static final int BATTLE_STATE_ATTACK = 3;
    public static final int BATTLE_STATE_DODGE = 4;
    public static final int BATTLE_STATE_RUN_AWAY = 5;
    public static final int WOUNDED_STATE_HURT = 6;
    public static final int WOUNDED_STATE_DEAD = 7;

    public static final int DECESION_READY = 9;

    private static final float DEFAULT_MAX_HEALTH = 100;
    private static final float CRITICAL_HEALTH_PERCENT = 5;

    private float maxHealth;
    private float health;
    private int aiState;
    private long elapsedTimeInState;
    private long elapsedTimeSinceDecision;
    private long timeSincePlayerLastSeen;
    private Vector3D startLocation;
    private boolean isRegenerating;

    private PolygonGroup blastModel;
    private CollisionDetection collisionDetection;
    protected Brain brain;

    // for displaying debug info only
    private boolean lastVisible;

    public AIBot(PolygonGroup polygonGroup,
        CollisionDetection collisionDetection, Brain brain,
        PolygonGroup blastModel)
    {
        super(polygonGroup);
        this.collisionDetection = collisionDetection;
        this.brain = brain;
        this.blastModel = blastModel;

        // random time until decision
        elapsedTimeSinceDecision =
            MoreMath.random((int)brain.decisionTime);
        maxHealth = DEFAULT_MAX_HEALTH;
        setHealth(maxHealth);
        aiState = NORMAL_STATE_IDLE;
        timeSincePlayerLastSeen = 10000;
    }


    public float getHealth() {
        return health;
    }

    public float getMaxHealth() {
        return maxHealth;
    }

    protected void setHealth(float health) {
        this.health = health;
    }


    /**
        Adds the specified amount to this bot's health. If the
        amount is less than zero, the bot's state is set to
        WOUNDED_STATE_HURT.
    */
    public void addHealth(float amount) {
        if (amount < 0) {
            if (health <= 0 || aiState == WOUNDED_STATE_HURT) {
                return;
            }
            MessageQueue.getInstance().debug(getName() + " hit");
            setAiState(WOUNDED_STATE_HURT, null);
            // make a decison in three seconds
            elapsedTimeSinceDecision = brain.decisionTime - 3000;
        }
        setHealth(health + amount);
    }


    /**
        Returns true if the health is critically low (less than
        CRITICAL_HEALTH_PERCENT).
    */
    public boolean isCriticalHealth() {
        return (health / maxHealth < CRITICAL_HEALTH_PERCENT/100);
    }

    /**
        Gets the AI state for this bot (different frm the
        GameObject state).
    */
    public int getAiState() {
        return aiState;
    }


    /**
        Sets the AI state for this bot (different from the
        GameObject state).
    */
    protected void setAiState(int aiState, GameObject player) {
        if (this.aiState == aiState) {
            return;
        }

        this.aiState = aiState;

        elapsedTimeInState = 0;
        PathFinder lastPattern = pathFinder;
        Vector3D playerLocation = null;
        if (player != null) {
            playerLocation = player.getLocation();
        }

        // update path
        switch (aiState) {
            case NORMAL_STATE_IDLE:
            case NORMAL_STATE_PATROL:
                setPathFinder(brain.idlePathFinder);
                setFacing(null);
                break;
            case NORMAL_STATE_CHASE:
                setPathFinder(brain.chasePathFinder);
                setFacing(null);
                break;
            case BATTLE_STATE_ATTACK:
                setPathFinder(brain.attackPathFinder);
                setFacing(playerLocation);
                break;
            case BATTLE_STATE_DODGE:
                setPathFinder(brain.dodgePathFinder);
                setFacing(null);
                break;
            case BATTLE_STATE_RUN_AWAY:
                setPathFinder(brain.runAwayPathFinder);
                setFacing(null);
                break;
            case WOUNDED_STATE_HURT:
                setPathFinder(null);
                setFacing(null);
                getTransform().stop();
                getTransform().setAngleVelocityY(
                    MoreMath.random(0.001f, 0.05f),
                    MoreMath.random(100, 500));
                break;
            case WOUNDED_STATE_DEAD:
                setPathFinder(null);
                setFacing(null);
                setJumping(true);
                getTransform().stop();
                Physics.getInstance().jumpToHeight(this, 16);
                getTransform().setAngleVelocityY(
                    MoreMath.random(0.001f, 0.05f),
                    MoreMath.random(100, 500));
                break;
            default:
                setPathFinder(null);
                setFacing(null);
                getTransform().stop();
                break;
        }

        if (lastPattern != pathFinder) {
            MessageQueue.getInstance().debug(
                getName() + " pattern: " + pathFinder);
        }
    }


    /**
        Returns true if this bot regenerates after it dies.
    */
    public boolean isRegenerating() {
        return isRegenerating;
    }


    /**
        Sets whether this bot regenerates after it dies.
    */
    public void setRegenerating(boolean isRegenerating) {
        this.isRegenerating = isRegenerating;
    }


    /**
        Sets the PathFinder class to use to follow the path.
    */
    public void setPathFinder(PathFinder pathFinder) {
        if (this.pathFinder != pathFinder) {
            super.setPathFinder(pathFinder);
            timeUntilPathRecalc = 0;
        }
    }

    /**
        Causes this bot to regenerate, restoring its location
        to its start location.
    */
    protected void regenerate() {
        setHealth(maxHealth);
        setState(STATE_ACTIVE);
        setAiState(DECESION_READY, null);
        getLocation().setTo(startLocation);
        getTransform().stop();
        setJumping(false);
        setPathFinder(null);
        setFacing(null);
        lastVisible = false;
        timeSincePlayerLastSeen = 10000;
        // let the game object manager know this object regenerated
        // (so collision detection from old location to new
        // location won't be performed)
        addSpawn(this);
    }

    public void update(GameObject player, long elapsedTime) {
        updateHelper(player, elapsedTime);
        super.update(player, elapsedTime);
    }

    public void updateHelper(GameObject player, long elapsedTime) {

        elapsedTimeSinceDecision+=elapsedTime;
        elapsedTimeInState+=elapsedTime;
        timeSincePlayerLastSeen+=elapsedTime;

        // record first location
        if (startLocation == null) {
            startLocation = new Vector3D(getLocation());
        }

        // regenerate if dead for 5 seconds
        if (aiState == WOUNDED_STATE_DEAD) {
            if (elapsedTimeInState >= 5000) {
                if (isRegenerating()) {
                    regenerate();
                }
                else {
                    setState(STATE_DESTROYED);
                }
            }
            return;
        }

        else if (aiState == WOUNDED_STATE_HURT) {
            if (elapsedTimeInState >= 500) {
                if (health <= 0) {
                    setAiState(WOUNDED_STATE_DEAD, player);
                    return;
                }
                else {
                    aiState = DECESION_READY;
                }
            }
            else {
                return;
            }
        }


        // run away if health critical
        if (isCriticalHealth() &&
            brain.runAwayPathFinder != null)
        {
            setAiState(BATTLE_STATE_RUN_AWAY, player);
            return;
        }


        // if idle and player visible, make decision every 500 ms
        if ((aiState == NORMAL_STATE_IDLE ||
            aiState == NORMAL_STATE_PATROL) &&
            elapsedTimeInState >= 500)
        {
            aiState = DECESION_READY;
        }

        // if time's up, make decision
        else if (elapsedTimeSinceDecision >= brain.decisionTime) {
            aiState = DECESION_READY;
        }

        // if done with current path, make decision
        else if (currentPath != null && !currentPath.hasNext() &&
            !getTransform().isMovingIgnoreY())
        {
            aiState = DECESION_READY;
        }

        // make a new decision
        if (aiState == DECESION_READY) {
            elapsedTimeSinceDecision = 0;

            if (canSee(player)) {
                setAiState(chooseBattleState(), player);
            }
            else if (timeSincePlayerLastSeen < 3000 ||
                canHear(player))
            {
                setAiState(NORMAL_STATE_CHASE, player);
            }
            else {
                setAiState(NORMAL_STATE_IDLE, player);
            }
        }
        // fire projectile
        else if (aiState == BATTLE_STATE_ATTACK &&
            elapsedTimeInState >= brain.aimTime &&
            brain.aimPathFinder != null)
        {
            elapsedTimeInState-=brain.aimTime;

            // longer aim time == more accuracy
            float p = Math.min(1, brain.aimTime / 2000f);
            ((AimPattern)brain.aimPathFinder).setAccuracy(p);
            Vector3D direction = (Vector3D)
                brain.aimPathFinder.find(this, player).next();
            fireProjectile(direction);
        }

    }

    /**
        Fires a projectile in the specified direction. The
        direction vector should be normalized.
    */
    public void fireProjectile(Vector3D direction) {

        Projectile blast = new Projectile(
            (PolygonGroup)blastModel.clone(),
            direction, this, 3, 6);
        float dist = 2 * (getBounds().getRadius() +
            blast.getBounds().getRadius());
        blast.getLocation().setTo(
            getX() + direction.x*dist,
            getY() + getBounds().getTopHeight()/2,
            getZ() + direction.z*dist);

        // "spawns" the new game object
        addSpawn(blast);

        // make a "virtual" noise that bots can "hear"
        // (500 milliseconds)
        makeNoise(500);
    }


    public int chooseBattleState() {
        float p = (float)Math.random();
        if (p <= brain.attackProbability) {
            return BATTLE_STATE_ATTACK;
        }
        else if (p <= brain.attackProbability +
            brain.dodgeProbability)
        {
            return BATTLE_STATE_DODGE;
        }
        else {
            return BATTLE_STATE_RUN_AWAY;
        }
    }

    /**
        Checks if this object can see the specified object,
        (assuming this object has eyes in the back of its head).
    */
    public boolean canSee(GameObject object) {
        // check if a line from this bot to the object
        // hits any walls
        boolean visible =
            (collisionDetection.getFirstWallIntersection(
            getX(), getZ(), object.getX(), object.getZ(),
            getY(), getY() + 1) == null);

        if (visible) {
            timeSincePlayerLastSeen = 0;
        }

        // display debug message
        if (visible != lastVisible) {
            String message = visible?" sees ":" no longer sees ";
            MessageQueue.getInstance().debug(
                getName() + message + object.getName());
            lastVisible = visible;
        }

        return visible;
    }


    /**
        Checks if this object can hear the specified object. The
        specified object must be making a noise and be within
        hearing range of this object.
    */
    public boolean canHear(GameObject object) {

        // check if object is making noise and this bot is not deaf
        if (!object.isMakingNoise() || brain.hearDistance == 0) {
            return false;
        }

        // check if this bot is close enough to hear the noise
        float distSq = getLocation().
            getDistanceSq(object.getLocation());
        float hearDistSq = brain.hearDistance * brain.hearDistance;
        boolean heard = (distSq <= hearDistSq);

        // display debug message
        if (heard) {
            MessageQueue.getInstance().debug(
                getName() + " hears " + object.getName());
        }

        return heard;
    }

    public boolean isFlying() {
        return (super.isFlying() && aiState != WOUNDED_STATE_DEAD);
    }

    public void notifyEndOfPath() {
        if (aiState != BATTLE_STATE_ATTACK) {
            setAiState(DECESION_READY, null);
        }
    }

    public void notifyHitPlayer(long damage) {
        // do nothing
    }

   public void notifyWallCollision() {
        if (aiState == BATTLE_STATE_RUN_AWAY) {
            getTransform().setVelocity(new Vector3D(0,0,0));
            setAiState(DECESION_READY, null);
        }
        else {
            super.notifyWallCollision();
        }
   }
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -