Okay, so I’m trying to make some AI. I don’t want the enemies to just attack you blindly, I want them to move around. So far, they dash here and there, jump now and then and attack randomly. It works, but it’s a bit buggy. However, I don’t want you to help me make it work flawlessly. I just want to know if the way I’m doing it is good or bad. The logic, the amount of code written for this task. Is it too much? Should it be more refined? I know I’ve done a lot of stuff in the wrong fashion, like the attack timers and stuff.
It might be a weird topic, but now I don’t want answers on what to code, I want guidelines on how to code. Lemme link you the source code for an enemy I have made. I’m pretty new to coding, and this is the first game I’m working on, just so you know.
Warning: A lot of code
package
{
import adobe.utils.CustomActions;
import flash.geom.Point;
import net.flashpunk.Entity;
import net.flashpunk.graphics.Spritemap;
import net.flashpunk.graphics.Image;
import net.flashpunk.utils.Input;
import net.flashpunk.utils.Key;
import net.flashpunk.FP;
import game_handling.Pause;
import Enemies;
public class Imp extends Enemies
{
//Gfx
[Embed(source = "assets/Imp_Full.png")] private const IMP:Class;
public var sprImp:Spritemap = new Spritemap(IMP, 120, 144);
//XP
private var xpDrop:int = 3;
//Stats
private var hp:int = 100;
public static const impStrength:uint = 10;
private var mainSpeed:Number = 1;
private var maxSpeed:Number = 4;
private var mediumSpeed:Number = 2;
private var jumpPower:Number = 14;
//Movement, physics
private var xSpeed:int = 0;
private var ySpeed:Number = 1;
private var gravity:Number = 0.3;
private var hFriction:Number = 0.95;
private var hgFriction:Number= 0.70;
private var vFriction:Number = 0.99;
private var jump:Boolean = false;
private var dash:String;
private var dashDecisionTimer:int = 60;
private var shouldDecideDash:Boolean = false;
private var dashCooldownTimer:int = 250;
private var attackTimer:int = 45;
private var isCloseEnough:Boolean = false;
private var isCloseEnoughRange:int = 600;
private var isCloseEnoughForDash:Boolean = false;
private var isCloseEnoughForDashRange:int = 200;
private var isAttacking:Boolean = false;
private var goingLeftHero:Boolean = false;
private var goingRightHero:Boolean = false;
private var isDashing:Boolean = false;
//Direction
private var goingLeft:Boolean = false;
private var goingRight:Boolean = false;
private var direction:String;
private var directionDecisionTimer:int = 60;
private var shouldDecideDirection:Boolean = false;
//Has weapon
private var hasWeapon:Boolean = false;
//Invincibility
private var invincibilityCounter:int = 0;
private var invincibilityCountDown:Boolean = false;
//Sight
private var sightRange:int = 480; //should be 980
private var followHero:Boolean = false;
//Roaming
private var shouldRoam:Boolean = false;
private var roam:String;
private var roamDecisionTimer:int = 0;
private var shouldDecideRoam:Boolean = false;
//Timer values && Decision values
//Timer values
private var attackTimerVal:uint = 45;
private var dashTimerVal:uint = 150;
private var dirTimerVal:uint = 100;
private var dashCooldownVal:uint = 250;
private var trueDash:uint = 500;
private var trueDashCD:uint = 500;
//Decision values
private var RoamDecisionVal:int = 100;
private var dashDecisionVal:int = 45;
private var dirDecisionVal:int = 100;
private var attackDecisionVal:int = 35;
//Nudge
private var nudgeCounter:int = 9;
private var nudgeCounterVal:int = 9;
public function Imp(p:Point)
{
//Spawn location
var p:Point;
x = p.x;
y = p.y;
//Gfx, hitbox
graphic = sprImp;
setHitbox(100, 128, -12, -16)
//Animations
sprImp.add("stand", [0], 0, true);
sprImp.add("run", [9, 10, 11, 12, 13, 14, 15, 16, 17], 20, true);
sprImp.add("crouch", [18, 19, 20, 21, 22, 23, 24, 25, 26], 30, true);
sprImp.add("jump", [27, 28, 29, 30, 31, 32, 33, 34, 35], 30, true);
sprImp.add("attack", [36, 37, 38, 39, 40, 41, 42, 43, 44], 15, false);
}
override public function update():void
{
if(Pause.PAUSED) return;
else
{
if (dashCooldownTimer == dashCooldownVal) isDashing = false;
//Damage hero
var e:Hero = collide("hero", x, y) as Hero;
if (e)
{
if (sprImp.currentAnim == "attack")
{
if (sprImp.index >= 4 && sprImp.index <= 7)
{
var damageGiven:Number = impStrength * HeroStats.armorPercentage;
Hero.takeDamage(damageGiven);
}
}
}
//Debug trace
//If hero is within range, follow him
if (Hero.xPos < x)
{
var leftDistance:Number = x - Hero.xPos;
if (leftDistance <= sightRange)
{
followHero = true;
sprImp.flipped = true;
}else {
followHero = false;
}
}else {
var rightDistance:Number = x - Hero.xPos;
if (rightDistance <= sightRange)
{
followHero = true;
sprImp.flipped = false;
//Close enough?
if (rightDistance >= 300) isCloseEnough = true;
else isCloseEnough = false;
}else {
followHero = false;
}
}
//Timers
//Deciding if the timers should increment
//Roaming
if (!followHero)
{
if (Math.floor(Math.random() * RoamDecisionVal) == 1) choose_roam();
}
//If the imp is following the hero
if (followHero)
{
//Dashing
if (Math.floor(Math.random() * dashDecisionVal) == 1)
{
dashDecisionTimer = 0;
}
//Direction
if (Math.floor(Math.random() * dirDecisionVal) == 1)
{
directionDecisionTimer = 0;
}
//Attack
if (Math.floor(Math.random() * attackDecisionVal) == 1 && attackTimer == attackTimerVal)
{
attackTimer = 0;
}
if (Math.floor(Math.random() * dashCooldownVal) == 0 && dashCooldownTimer == dashCooldownVal && trueDash == trueDashCD && isCloseEnoughForDash)
{
choose_dash();
}
}
//Executing the attack action
if (attackTimer != attackTimerVal && isCloseEnough)
{
isAttacking = true;
}else {
isAttacking = false;
}
if (directionDecisionTimer == 0) choose_direction();
//Incrementing the timers
if (attackTimer != attackTimerVal) attackTimer ++;
if (dashDecisionTimer != dashTimerVal) dashDecisionTimer ++;
if (directionDecisionTimer != dirTimerVal) directionDecisionTimer ++;
if (dashCooldownTimer != dashCooldownVal) dashCooldownTimer ++;
//Collision
var sword:Weapon_Sword_Sword_of_the_AntiMage = collide("weapon", x, y) as Weapon_Sword_Sword_of_the_AntiMage;
var hero:Hero = collide("hero", x, y) as Hero;
//Getting hit by sword
/*if (sword)
{
if (HeroStats.hp > 0)
{
if (invincibilityCounter == 0)
{
if (sword.sprSword.currentAnim == "attack")
{
var damageTaken:Number = HeroStats.strength * 10;
var xLocation:Number = x + this.width / 2;
var a:Number = Math.floor(Math.random() * (1 + x + this.width - x)) + x;
var b:Number = Math.floor(Math.random() * (1 + y + 10 - y)) + y;
hp -= damageTaken;
invincibilityCountDown = true;
FP.world.add(new DamageShower(a, b, damageTaken, "0xFF0000"));
}
}
}
}*/
//Invincibility
if (invincibilityCountDown)
{
invincibilityCounter ++;
if (invincibilityCounter == 15)
{
invincibilityCounter = 0;
invincibilityCountDown = false;
}
}
//Death
if (y > 1080)
{
FP.world.remove(this);
}
if (hp <= 0)
{
var orb:XpOrb = new XpOrb(x, y, xpDrop);
FP.world.add(orb);
FP.world.remove(this);
}
//Roam
if (!followHero)
{
if (roam == "Go left")
{
xSpeed = - mainSpeed;
goingLeft = true;
}
if (roam == "Go right")
{
xSpeed = mainSpeed;
goingRight = true;
}
if (roam == "Stand still") xSpeed = 0;
if (roam == "Jump") jump = true;
}
//Follow hero
if (followHero)
{
//If hero is to the left, then right
if (Hero.xPos < x)
{
if(x > Hero.xPos + 100)
{
xSpeed = 0 - ((x - Hero.xPos) / 55)
goingLeftHero = true;
}else {
goingLeftHero = false;
xSpeed = 0;
}
}
else {
if(x < Hero.xPos - 100)
{
xSpeed = (Hero.xPos - x) / 55
goingRightHero = true;
}else {
goingRightHero = false;
xSpeed = 0;
}
}
}
//Gravity, bitch
if(!collide("ground", x, y + 1))
{
ySpeed+=gravity;
}
//Facing whatever direction
if (goingLeft) sprImp.flipped = true;
if (goingRight) sprImp.flipped = false;
//Attack
if (attackTimer != attackTimerVal)
{
isAttacking = true;
}
if (attackTimer == attackTimerVal)
{
isAttacking = false;
}
//Attack animation
if (isAttacking)
{
sprImp.play("attack");
}else {
if(goingLeft || goingRight) sprImp.play("run")
if(!collide("ground",x,y)) sprImp.play("jump")
if(isDashing) sprImp.play("run")
}
//Flip
if (followHero)
{
if (x < Hero.xPos) sprImp.flipped = false;
else sprImp.flipped = true;
}
//Move back if touching hero
if (x > Hero.xPos)
{
if (x - Hero.xPos <= isCloseEnoughRange) isCloseEnough = true;
else isCloseEnough = false;
if (x - Hero.xPos <= isCloseEnoughForDashRange) isCloseEnoughForDash = true;
else isCloseEnoughForDash = false;
if (x - Hero.xPos <= 80) xSpeed = maxSpeed /2;
}else {
if (Hero.xPos - x <= isCloseEnoughRange) isCloseEnough = true;
else isCloseEnough = false;
if (Hero.xPos - x <= isCloseEnoughForDashRange) isCloseEnoughForDash = true;
else isCloseEnoughForDash = false;
if (Hero.xPos - x <= 80) xSpeed = - (maxSpeed / 2)
}
//True dash
//Dash
if (isDashing && followHero)
{
if (isCloseEnough)
{
if (dash == "Dash right") xSpeed = maxSpeed;
if (dash == "Dash left") xSpeed = - maxSpeed;
if (dash == "Dash left hard") xSpeed = maxSpeed*1.5;
if(dash == "Dash right hard") xSpeed = -(maxSpeed *1.5);
}
}
//True dash cd
if (trueDash != trueDashCD) trueDash ++;
if (followHero)
{
if (Math.floor(Math.random() * 150) == 1)
{
jump = true;
}
else jump = false;
}
if (jump)
{
if (collide("ground", x, y + 1))
{
ySpeed-=jumpPower;
}
}
//Nudge
if (nudgeCounter != nudgeCounterVal)
{
if (Hero.xPos > x)
{
xSpeed = - maxSpeed * 4;
}else {
xSpeed = maxSpeed * 4;
}
ySpeed -= jumpPower / 20;
nudgeCounter ++;
}
//Adjust the movement
adjustXPosition();
adjustYPosition();
}
}
override public function takeDamage(damageTaken:int):void
{
if (invincibilityCounter == 0)
{
nudgeCounter = 0;
var xLocation:Number = x + this.width / 2;
var a:Number = Math.floor(Math.random() * (1 + x + this.width - x)) + x;
var b:Number = Math.floor(Math.random() * (1 + y + 10 - y)) + y;
hp -= damageTaken;
invincibilityCountDown = true;
FP.world.add(new DamageShower(a, b, damageTaken, "0xFF0000", 40));
}
}
private function adjustXPosition():void
{
for (var i:int=0; i<Math.abs(xSpeed); i++) {
if (! collide("ground",x+FP.sign(xSpeed),y)) {
x += FP.sign(xSpeed);
} else {
xSpeed = 0;
break;
}
}
}
private function adjustYPosition():void {
for (var i:int=0; i<Math.abs(ySpeed); i++) {
if (! collide("ground", x, y + FP.sign(ySpeed))) {
y += FP.sign(ySpeed);
} else {
ySpeed=0;
break;
}
}
}
private function choose_roam():void
{
roam = FP.choose("Go left", "Go right", "Stand still", "Stand still", "Stand still", "Jump");
}
private function choose_dash():void
{
if (dashCooldownTimer == dashCooldownVal)
{
dashCooldownTimer = 0;
isDashing = true;
}
dash = FP.choose("Dash right", "Dash left", "Dash right hard", "Dash left hard");
}
private function choose_direction():void
{
direction = FP.choose("Left", "Right", "Stand still")
}
}
}