# _*_ coding:utf-8 _*_
from random import randint
import sys
import pygame
from pygame.locals import *
from gameobjects.vector2 import Vector2
import time
__author__ = 'admin'
'''
蚂蚁状态机(十三)
新目标:若蜘蛛死亡,将蜘蛛运回巢中
objects_dict中就不能将health为0的蜘蛛删除了,需要保留
hunt中将蜘蛛杀死后也进入ant的trans状态
改造trans状态的方法,在里面判断被携带的对象是leaf还是spider
'''
SCREEN_SIZE = (640, 480)
NEST_POSITION = (320, 240)
NEST_SIZE = 100.
WHITE = (255, 255, 255)
clock = pygame.time.Clock()
class Microcosm(object):
def __init__(self):
self.objects_dict = {}
self.capricious_dict = {}
self.obj_id = 0
self.capricious_list = []
def addObj(self, obj):
# 在changeState方法中需要传入一个包含所有蚂蚁对象的列表,这个列表中也包含有未被拾取的叶子
self.capricious_list.append(obj)
# 用于存储程序运行之后生成的所有对象及其对应的id
self.objects_dict[self.obj_id] = obj
# 道理同objects_dict,只不过该dict会不断的被添加和删除,主要是确定哪些叶子允许被蚂蚁seek
self.capricious_dict[self.obj_id] = obj
# 是在这个位置为对象附上id属性的,下面删除字典元素时要用到这个id
obj.id = self.obj_id
self.obj_id += 1
def getObj(self, obj_id):
return self.objects_dict[obj_id]
def get_closeTo_obj(self, name, position, range=50):
for obj in self.capricious_dict.values():
if obj.name == name:
distance = position.get_distance_to(obj.position)
if distance <= range:
return obj
return None
def get_obj_InNest(self, name):
for obj in self.capricious_dict.values():
if obj.name == name:
distance = obj.position.get_distance_to(NEST_POSITION)
if distance < NEST_SIZE:
return obj
return None
# 绘制方法存在这里,每个继承的子类都在这里绘制,无需在每个对象类中再绘制了
# 每个对象类中只需要实时的变更其位置即可
def draw(self, surface):
surface.fill(WHITE)
pygame.draw.circle(surface, (200, 255, 200), NEST_POSITION, int(NEST_SIZE))
for obj in self.objects_dict.values():
obj.draw(surface)
class ObjProduct(object):
def __init__(self, microcosm, name, path):
self.microcosm = microcosm
self.name = name
self.image_path = path
self.image = pygame.image.load(self.image_path).convert_alpha()
self.state = 0
self.fsm = None
self.id = 0
self.speed = 0
self.position = Vector2(0, 0)
self.destination = Vector2(0, 0)
def bind(self, state, fsm):
self.fsm = fsm
self.state = state
def draw(self, surface):
x, y = self.position
w, h = self.image.get_size()
surface.blit(self.image, (x - w / 2, y - h / 2))
def explore(self):
print("[%s-%d]探索中....." % (self.name, self.id))
distance = self.destination - self.position
# 获取此时蚂蚁距离目标点间的向量长度(即两点间的实际距离)
x = self.position.get_distance_to(self.destination)
# 获取单位向量,即每次蚂蚁移动的向量
heading = distance.normalise()
# 如果蚂蚁再移动单位向量就会越过目标点
if x <= heading.get_length():
print("[%s-%d]到达目的地了....前往下一个点" % (self.name, self.id))
# self.destination = Vector2(randint(0, SCREEN_SIZE[0] // 2), randint(0, SCREEN_SIZE[1] // 2))
return True
else:
self.position += heading
class Spider(ObjProduct):
def __init__(self, microcosm, name, path):
ObjProduct.__init__(self, microcosm, name, path)
self.position = Vector2(randint(0, SCREEN_SIZE[0] // 2), randint(0, SCREEN_SIZE[1] // 2))
self.health = 25
def draw(self, surface):
ObjProduct.draw(self, surface)
# 为蜘蛛增加血条,依据health进行血量调整
x, y = self.position
w, h = self.image.get_size()
blood_x = x - w / 2
blood_y = y + h / 2
screen.fill((255, 0, 0), (blood_x, blood_y, 25, 4))
screen.fill((0, 255, 0), (blood_x, blood_y, self.health, 4))
def wait(self):
print("[%s-%d]停顿下,思考人生....." % (self.name, self.id))
pygame.time.delay(10)
class Leaf(ObjProduct):
def __init__(self, microcosm, name, path):
ObjProduct.__init__(self, microcosm, name, path)
self.position = Vector2(randint(0, SCREEN_SIZE[0] // 2), randint(0, SCREEN_SIZE[1] // 2))
def draw(self, surface):
ObjProduct.draw(self, surface)
class Ant(ObjProduct):
def __init__(self, microcosm, name, path):
ObjProduct.__init__(self, microcosm, name, path)
self.position = Vector2(NEST_POSITION)
self.destination = Vector2(randint(0, SCREEN_SIZE[0] // 2), randint(0, SCREEN_SIZE[1] // 2))
# 这里让leaf_id为None,单纯就是不想给它一个数字而已,避免这个默认值作为Key使用时报错
self.leaf_id = None
# 这里让leaf_id为None,单纯就是不想给它一个数字而已,避免这个默认值作为Key使用时报错
self.spider_id = None
def draw(self, surface):
ObjProduct.draw(self, surface)
def wait(self):
print("[%s-%d]停顿下,思考人生....." % (self.name, self.id))
pygame.time.delay(10)
class State(object):
def exec(self, obj):
pass
def exit(self, obj):
pass
class ExploreState(State):
def exec(self, obj):
# 决定探索过程中触发wait的频率
if randint(1, 1000) == 1:
# 信号置为0
obj.state = 0
else:
if obj.name == 'ant':
# 检测此时是否有蜘蛛进入巢穴
spider_ = obj.microcosm.get_obj_InNest('spider')
# 如果有这样的蜘蛛对象
if spider_ is not None:
# 为当前蚂蚁对象添加该目标蜘蛛id属性(将该蜘蛛绑定到该蚂蚁上,方便调用)
obj.spider_id = spider_.id
obj.state = 4
else:
# 判断当前蚂蚁对象附近范围内是否有叶子存在,并获取这个叶子对象
leaf_ = obj.microcosm.get_closeTo_obj('leaf', obj.position)
# 如果有这样的叶子对象
if leaf_ is not None:
# 为当前蚂蚁对象添加该目标叶子id属性(将该叶子绑定到该蚂蚁上,方便调用)
obj.leaf_id = leaf_.id
# 信号置为seek
obj.state = 2
else:
# 附近没有叶子就执行explore
if obj.explore():
obj.destination = Vector2(randint(0, SCREEN_SIZE[0] // 2), randint(0, SCREEN_SIZE[1] // 2))
elif obj.name == 'spider':
if obj.explore():
obj.destination = Vector2(randint(0, SCREEN_SIZE[0] // 2), randint(0, SCREEN_SIZE[1] // 2))
class WaitSate(State):
def exec(self, obj):
# 执行wait方法
obj.wait()
# 信号置为1,使得蚂蚁再动起来
obj.state = 1
class SeekState(State):
def exec(self, obj):
# 当前蚂蚁对象是否绑定了leaf_id属性(这点可以说明蚂蚁附近是否有叶子)
if obj.leaf_id is not None:
# 当前蚂蚁已经有了叶子目标,capricious_dict中该叶子还未被删除,说明未被其他蚂蚁抢先拾取
if obj.leaf_id in obj.microcosm.capricious_dict.keys():
print("[%s-%d]发现目标叶子..走过去" % (obj.name, obj.id))
# 通过已知的叶子id获取对应的叶子对象
leaf_ = obj.microcosm.getObj(obj.leaf_id)
# 将叶子的位置当做蚂蚁本次行为的终点
obj.destination = leaf_.position
if obj.explore():
print("[%s-%d]已达到目标叶子" % (obj.name, obj.id))
# 将该叶子移出capricious_dict中,起到不可被seek的作用
obj.microcosm.capricious_dict.pop(leaf_.id)
obj.microcosm.capricious_list.remove(leaf_)
# 将巢穴范围内随机的一个位置当做蚂蚁TransState的终点
obj.destination = Vector2(NEST_POSITION) + Vector2(randint(-40, 40), randint(-40, 40))
obj.state = 3
else:
# 当前蚂蚁锁定的叶子被其他蚂蚁抢先拾取
print("[%s-%d]的叶子被别的蚂蚁抢先拾取...." % (obj.name, obj.id))
# 叶子被别的蚂蚁抢先拾取,则把该蚂蚁绑定的叶子id置为None吧
obj.leaf_id = None
obj.state = 1
else:
# 当前蚂蚁附近没有叶子了,执行探索
obj.state = 1
class TransState(State):
def exec(self, obj):
global prey, prey_id
# prey意为猎物,这里代指叶子或蜘蛛
if obj.leaf_id is not None:
prey = obj.microcosm.getObj(obj.leaf_id)
prey_id = obj.leaf_id
elif obj.spider_id is not None:
prey = obj.microcosm.getObj(obj.spider_id)
prey_id = obj.spider_id
print("[%s-%d]拾起[%s],走回巢穴..." % (obj.name, obj.id, prey.name))
# 通过蚂蚁当前的位置确定其拾取到的叶子的位置-----》待优化
prey.position = Vector2(obj.position)
# 获取此时蚂蚁起点->终点的向量
distance = obj.destination - obj.position
# 获取此时蚂蚁距离目标点间的向量长度(即两点间的实际距离)
x = obj.position.get_distance_to(obj.destination)
# 获取单位向量,即每次蚂蚁移动的向量
heading = distance.normalise()
# 如果蚂蚁再移动单位向量就会越过目标点
if x <= heading.get_length():
# print("[Ant-%d]已回到巢穴之中" % obj.id)
# 将该蚂蚁绑定的叶子id置为None
prey_id = None
# 信号置为1,继续探索
obj.state = 1
else:
obj.position += heading
# 还未将叶子运回巢穴之内,继续trans
obj.state = 3
class HuntState(State):
def exec(self, obj):
# 当前蚂蚁已经有了蜘蛛目标,capricious_dict中该蜘蛛还未被删除,说明该蜘蛛还未死亡
if obj.spider_id in obj.microcosm.capricious_dict.keys():
print("[%s-%d]发现目标蜘蛛..走过去" % (obj.name, obj.id))
spider_ = obj.microcosm.getObj(obj.spider_id)
obj.destination = spider_.position
if obj.explore():
print("[%s-%d]已经追击到蜘蛛" % (obj.name, obj.id))
# 这里模拟攻击状态--------》待优化
if randint(1, 1) == 1:
print("[%s-%d]正在攻击蜘蛛" % (obj.name, obj.id))
spider_.health -= 1
if spider_.health <= 0:
# 将该叶子移出capricious_dict中,起到不可被seek的作用,这里其实也应该将objects_dict中的该蜘蛛删除
obj.microcosm.capricious_dict.pop(spider_.id)
obj.microcosm.capricious_list.remove(spider_)
# obj.microcosm.objects_dict.pop(spider_.id)
# 将巢穴范围内随机的一个位置当做蚂蚁TransState的终点
obj.destination = Vector2(NEST_POSITION) + Vector2(randint(-40, 40), randint(-40, 40))
# 蜘蛛已死,开始搬运死蜘蛛回巢
obj.state = 3
else:
# 该侵犯巢穴的蜘蛛未死亡,继续hunt
obj.state = 4
else:
# 当前蚂蚁锁定的蜘蛛被其他蚂蚁抢先杀死
print("[%s-%d]锁定的蜘蛛已被其他蚂蚁杀死...." % (obj.name, obj.id))
# 蜘蛛被别的蚂蚁杀死,则把该蚂蚁绑定的蜘蛛id置为None吧
obj.spider_id = None
# 进入explore
obj.state = 1
class StateMachine(object):
def __init__(self):
# 状态集合
self.states_Ant = {0: WaitSate(), 1: ExploreState(), 2: SeekState(), 3: TransState(), 4: HuntState()}
self.states_Spider = {0: WaitSate(), 1: ExploreState()}
# 改变状态
def changeState(self, objs):
for obj in objs:
# 这里先留着吧,考虑下state是否有为None的必要
if obj.state is None:
return
else:
if obj.name == 'leaf':
# 无状态,不做处理,其实也可以在外层先过滤掉leaf对象
pass
elif obj.name == 'spider':
# print("name[%s]--state[%d]" % (obj.name, obj.state))
newFsm = self.states_Spider[obj.state]
newFsm.exec(obj)
else:
# print("name[%s]--state[%d]" % (obj.name, obj.state))
newFsm = self.states_Ant[obj.state]
newFsm.exec(obj)
def checkForOut():
for event in pygame.event.get():
if event.type == 12:
sys.exit()
if event.type == 2:
if event.key == 27:
exit()
pygame.init()
screen = pygame.display.set_mode(SCREEN_SIZE, 0, 32)
micr = Microcosm()
sm = StateMachine()
for i in range(10):
ant = Ant(micr, 'ant', r"E:\PycharmProjects\PGStudy\resource\ant.png")
ant.bind(0, sm.states_Ant[0])
micr.addObj(ant)
while True:
checkForOut()
# 生出叶子
if randint(1, 50) == 1:
leaf = Leaf(micr, 'leaf', r"E:\PycharmProjects\PGStudy\resource\leaf.png")
micr.addObj(leaf)
if randint(1, 1000) == 1:
spider = Spider(micr, 'spider', r"E:\PycharmProjects\PGStudy\resource\spider.png")
micr.addObj(spider)
# 将绘制方法全部封装,每次循环重刷屏幕上所有对象
micr.draw(screen)
sm.changeState(micr.capricious_list)
pygame.display.update()
pygame.time.delay(10)