An enemy isn't too threatening if they're just standing there menacingly. I'll add some basic AI to make things more interesting
The most basic type of AI for a video game enemy is to follow the player. I'll create a function called "Hunter" that requires two objects input: a hunter and target.
def Hunt(hunter, target):
if hunter.y>target.y:
if hunter.x>target.x: #Move Up-Left
hunter.x -= hunter.speed * 0.707
hunter.y -= hunter.speed * 0.707
elif hunter.x<target.x: #Move Up-Right
hunter.x += hunter.speed * 0.707
hunter.y -= hunter.speed * 0.707
else: #Move Up
hunter.y -= hunter.speed
elif hunter.y<target.y:
if hunter.x>target.x: #Move Down-Left
hunter.x -= hunter.speed * 0.707
hunter.y += hunter.speed * 0.707
elif hunter.x<target.x: #Move Down-Right
hunter.x += hunter.speed * 0.707
hunter.y += hunter.speed * 0.707
else: #Move Down
hunter.y += hunter.speed
elif hunter.x>target.x: #Move Left
hunter.x -= hunter.speed
elif hunter.x<target.x: #Move Right
hunter.x += hunter.speed
This is obviously derived from the "Move_Object" function used for making moving the player object around. Instead of key inputs as triggers, the "Hunt" function reacts to the game conditions. If the target is to it's left (target.x < hunter.x), the hunter will move left (hunter.x -= hunter.speed), and so on for any orientation. Evidently, I also added the "speed" variable to the "Enemy" class so I can adjust for the different enemy speeds.
while True: frames = 30 clock=pygame.time.Clock() clock.tick(frames) for event in pygame.event.get(): #Exit game on red button
if event.type == pygame.QUIT:
return
win.fill((0,0,0))
Move_Object(Red_Guy)
pygame.draw.rect(win, Red_Guy.color, (Red_Guy.x, Red_Guy.y, Red_Guy.width, Red_Guy.height))
Shoot(Red_Guy)
for bullet in Red_Guy.bullets:
bullet.Move(win)
Collision(Red_Guy, bullet, Blue_Guy)
if Blue_Guy.health>0:
pygame.draw.rect(win, Blue_Guy.color, (Blue_Guy.x, Blue_Guy.y, Blue_Guy.width, Blue_Guy.height))
Hunt(Blue_Guy, Red_Guy)
By adding the "Hunt" function into the While loop, the "Blue_Guy" springs to life! This is a very primitive type of AI movement since it's just taking the most direct path to the player. This does not even consider pathfinding around level obstacles or other object collision. But this will work for now.
Hit Invulnerability
When the the hunter finally reaches it's target, something needs to happen. Typically, if the enemy touches the player (their hitboxes collide), the player takes a hit. Unlike the previous collisions which use a single coordinate, both objects have their own widths and heights. Whenever the two object's rectangles overlap, there is a collision.
This means a variety of conditions need to be considered to trigger the collision:
Hit Invulnerability
Once the trigger is collision, the "Hit" attribute for the target object will be made "True". If the "Hit"
attribute is true, then the damage is applied (reducing "Health" attribute) and "Hit" is returned to "False". Similar to the bullet creation, conditions will trigger as fast as the cycle time, so a "cooldown" needs to be added. Many games have these "invincibility" frames so that each hit will only apply damage once.
To address this, applying the damage will also turn on object invulnerability ("Invul") to give a grace period. This "Invul" state will need to be "False" for "Hunt" and "Object_Collision" functions to trigger. That is, no the enemy won't hunt or damage the player while they are invulnerable. Another function called "Invulnerable" also needs to added. It simply ticks down an object's "Invul" attribute when it's above zero.
I Get Knocked Down
Lastly, we can add a bit of "knock-back" so to give more impact to the enemy collision. This is done by adjusting the target's coordinates when the "Hit" state is triggered, based on direction. Getting hit from the left moves the player to the right, etc.
When I tested it without, the player object would get knocked diagonally when the enemy was very slightly off-level. Since these diagonal calculations were most often used, I had to add an additional threshold to calculate the which direction to use. It's a calculation that determines if the difference in x or y coordinate is above a certain value.
Final Code
The code is getting rather lengthy now, and I've had to make several adjustments to the attributes. See below for the complete code:
import pygame class Characters(object): def __init__(self, x, y, width, height, color): self.x = x self.y = y self.width = width self.height = height self.color = color self.direction = 'Left' self.bullets=[] self.cooldown = 0 self.invul = 0 self.health = 1 self.hit = False class Enemies(object): def __init__(self,x,y, width, height, color): self.x = x self.y = y self.width = width self.height = height self.color = color self.health = 1 self.speed = 0.75 def Move_Object(obj): speed = 1 if pygame.key.get_pressed()[pygame.K_w]: if pygame.key.get_pressed()[pygame.K_a]: #Move Up-Left obj.x -= speed * 0.707 obj.y -= speed * 0.707 obj.direction = 'Up Left' elif pygame.key.get_pressed()[pygame.K_d]: #Move Up-Right obj.x += speed * 0.707 obj.y -= speed * 0.707 obj.direction = 'Up Right' else: #Move Up obj.y -= speed obj.direction = 'Up' elif pygame.key.get_pressed()[pygame.K_s]: if pygame.key.get_pressed()[pygame.K_a]: #Move Down-Left obj.x -= speed * 0.707 obj.y += speed * 0.707 obj.direction = 'Down Left' elif pygame.key.get_pressed()[pygame.K_d]: #Move Down-Right obj.x += speed * 0.707 obj.y += speed * 0.707 obj.direction = 'Down Right' else: #Move Down obj.y += speed obj.direction = 'Down' elif pygame.key.get_pressed()[pygame.K_a]: #Move Left obj.x -= speed obj.direction = 'Left' elif pygame.key.get_pressed()[pygame.K_d]: #Move Right obj.x += speed obj.direction = 'Right' if obj.x < 0: #If object exceeds left boundary obj.x = 0 elif obj.x > 400-5: #If object exceeds right boundary obj.x = 400-5 if obj.y < 0: #If object exceeds top boundary obj.y = 0 elif obj.y > 300-10: #If object exceeds bottom boundary obj.y = 300-10 class Projectiles(object): def __init__(self, x, y, direction, color, speed): self.x = x self.y = y self.color = color self.direction = direction self.speed = speed self.radius = 3 def Move(self, win): if self.direction == 'Up Left': self.x -= self.speed * 0.707 self.y -= self.speed * 0.707 elif self.direction == 'Up': self.y -= self.speed elif self.direction == 'Up Right': self.x += self.speed * 0.707 self.y -= self.speed * 0.707 elif self.direction == 'Right': self.x += self.speed elif self.direction == 'Down Right': self.x += self.speed * 0.707 self.y += self.speed * 0.707 elif self.direction == 'Down': self.y += self.speed elif self.direction == 'Down Left': self.x -= self.speed * 0.707 self.y += self.speed * 0.707 elif self.direction == 'Left': self.x -= self.speed pygame.draw.circle(win, self.color, (round(self.x), round(self.y)), self.radius) def Shoot(obj): if pygame.key.get_pressed()[pygame.K_j]: if obj.cooldown == 0: obj.bullets.append(Projectiles(obj.x, obj.y, obj.direction, (255,255,255), 0.05)) obj.cooldown = 2500 if obj.cooldown>0: obj.cooldown -= 1 def Collision(obj, proj, enemy): if proj.x < 0: #If object exceeds left boundary obj.bullets.pop(obj.bullets.index(proj)) elif proj.x > 400-proj.radius: #If object exceeds right boundary obj.bullets.pop(obj.bullets.index(proj)) if proj.y < 0: #If object exceeds top boundary obj.bullets.pop(obj.bullets.index(proj)) elif proj.y > 300-proj.radius: #If object exceeds bottom boundary obj.bullets.pop(obj.bullets.index(proj)) if enemy.health>0: if proj.x > enemy.x and proj.x < enemy.x+enemy.width: if proj.y > enemy.y and proj.y < enemy.y+enemy.height: obj.bullets.pop(obj.bullets.index(proj)) enemy.health -= 1 def Object_Collision(hunter, target): knock_back = 10 if not target.invul: if hunter.x+hunter.width>target.x and hunter.x<target.x: #Right boundary if hunter.y<target.y+target.height and hunter.y+hunter.height>target.y+target.height: target.hit = True #Hit from bottom left elif hunter.y+hunter.height>target.y and hunter.y<target.y: target.hit = True #Hit from top left elif hunter.y == target.y or hunter.y+hunter.height == target.y+target.height: target.hit = True #Hit from left elif hunter.x+hunter.width>target.x+target.width and hunter.x<target.x+target.width: #Left boundary if hunter.y<target.y+target.height and hunter.y+hunter.height>target.y+target.height: target.hit = True #Hit from bottom right elif hunter.y+hunter.height>target.y and hunter.y<target.y: target.hit = True #Hit from top right elif hunter.y == target.y or hunter.y+hunter.height == target.y+target.height: target.hit = True #Hit from right elif hunter.y+hunter.height>target.y+target.height and hunter.y<target.y+target.height: if hunter.x == target.x or hunter.x+hunter.width == target.x+target.width: target.hit = True #Hit from bottom elif hunter.y+hunter.height>target.y and hunter.y<target.y: #Bottom boundary if hunter.x == target.x or hunter.x+hunter.width == target.x+target.width: target.hit = True #Hit from top else: target.hit = False if target.hit: target.invul = 1000 target.health -= 1 target.hit = False if hunter.x - target.x > 5: #Knock left if hunter.y - target.y > 5: #Knock up left target.x -= knock_back *.707 target.y -= knock_back *.707 elif target.y - hunter.y > 5: #Knock down left target.x -= knock_back *.707 target.y += knock_back *.707 else: target.x -= knock_back elif target.x - hunter.x > 5: #Knock right if hunter.y - target.y > 5: #Knock up right target.x += knock_back *.707 target.y -= knock_back *.707 elif target.y - hunter.y > 5: #Knock down right target.x += knock_back *.707 target.y += knock_back *.707 else: target.x += knock_back elif hunter.y - target.y > 5: #Knock up target.y -= knock_back elif target.y - hunter.y > 5: #Knock down target.y += knock_back else: target.y += knock_back def Invulnerable(obj): if obj.invul > 0: obj.invul -= 1 def Hunt(hunter, target): if not target.invul: if hunter.y>target.y: if hunter.x>target.x: #Move Up-Left hunter.x -= hunter.speed * 0.707 hunter.y -= hunter.speed * 0.707 elif hunter.x<target.x: #Move Up-Right hunter.x += hunter.speed * 0.707 hunter.y -= hunter.speed * 0.707 else: #Move Up hunter.y -= hunter.speed elif hunter.y<target.y: if hunter.x>target.x: #Move Down-Left hunter.x -= hunter.speed * 0.707 hunter.y += hunter.speed * 0.707 elif hunter.x<target.x: #Move Down-Right hunter.x += hunter.speed * 0.707 hunter.y += hunter.speed * 0.707 else: #Move Down hunter.y += hunter.speed elif hunter.x>target.x: #Move Left hunter.x -= hunter.speed elif hunter.x<target.x: #Move Right hunter.x += hunter.speed def main(): win = pygame.display.set_mode((400, 300)) pygame.init() Red_Guy = Characters(100, 100, 10, 20, (255, 0, 0)) Blue_Guy = Enemies(200, 100, 10, 20, (0, 0, 255)) bullets = [] while True: frames = 30 clock=pygame.time.Clock() clock.tick(frames)
for event in pygame.event.get(): #Exit game on red button if event.type == pygame.QUIT: return win.fill((0,0,0)) Move_Object(Red_Guy) pygame.draw.rect(win, Red_Guy.color, (Red_Guy.x, Red_Guy.y, Red_Guy.width, Red_Guy.height)) Shoot(Red_Guy) for bullet in Red_Guy.bullets: bullet.Move(win) Collision(Red_Guy, bullet, Blue_Guy) if Blue_Guy.health>0: pygame.draw.rect(win, Blue_Guy.color, (Blue_Guy.x, Blue_Guy.y, Blue_Guy.width, Blue_Guy.height)) Hunt(Blue_Guy, Red_Guy) Object_Collision(Blue_Guy, Red_Guy) Invulnerable(Red_Guy) pygame.display.flip() main()
Comments
Post a Comment