PyGmae:有限状态机实践(八)

#   _*_ coding:utf-8 _*_
from random import randint
import sys
import pygame
from pygame.locals import *
from gameobjects.vector2 import Vector2
import time

__author__ = 'admin'

'''
    蚂蚁状态机(八)
    新目标:蚂蚁将叶子搬回巢穴范围即可,无需到达NEST_POSITION
    解决:需要在TransState增加对终点destination的判断
          换句话说,TransState中蚂蚁的终点需要是巢穴范围内的随机一点
          这里的巢穴是圆形,那么目标就是如何获取圆中的随机一点
          采取在圆心上增加随机向量的方式
    问题:TransState修改代码如下
          obj.destination = Vector2(NEST_POSITION) + Vector2(randint(-20, 20), randint(-20, 20))
          由于蚂蚁一旦处于TransState,只要为走到巢穴内会一直处于该状态
          这样就会造成每次执行该状态destination都会变一次
          界面的表现就是,蚂蚁在靠近圆心的位置不停抖动
    解决:所以destination一定要在蚂蚁进入TransState前就定下来
'''
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 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))

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(100, 100)
        self.destination = Vector2(randint(0, SCREEN_SIZE[0] // 2), randint(0, SCREEN_SIZE[1] // 2))
        #   这里让leaf_id为None,单纯就是不想给它一个数字而已,避免这个默认值作为Key使用时报错
        self.leaf_id = None

    def draw(self, surface):
        ObjProduct.draw(self, surface)

    def explore(self):
        print("[Ant-%d]探索中....." % self.id)
        distance = self.destination - self.position
        #   获取此时蚂蚁距离目标点间的向量长度(即两点间的实际距离)
        x = self.position.get_distance_to(self.destination)
        #   获取单位向量,即每次蚂蚁移动的向量
        heading = distance.normalise()
        #   如果蚂蚁再移动单位向量就会越过目标点
        if x <= heading.get_length():
            print("[Ant-%d]到达目的地了....前往下一个点" % self.id)
            self.destination = Vector2(randint(0, SCREEN_SIZE[0] // 2), randint(0, SCREEN_SIZE[1] // 2))
        else:
            self.position += heading

    def wait(self):
        print("[Ant-%d]停顿下,思考人生....." % 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:
            #   判断当前蚂蚁对象附近范围内是否有叶子存在,并获取这个叶子对象
            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
                obj.explore()

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():
                #   通过已知的叶子id获取对应的叶子对象
                leaf_ = obj.microcosm.getObj(obj.leaf_id)
                print("[Ant-%d]发现目标叶子..走过去" % obj.id)
                #   将叶子的位置当做蚂蚁本次行为的终点
                obj.destination = leaf_.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)
                    #   将该叶子移出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
                #   蚂蚁还未到达叶子,即还需要继续走一个heading
                else:
                    obj.position += heading
                    #   继续执行seeking
                    obj.state = 2
            else:
                #   当前蚂蚁锁定的叶子被其他蚂蚁抢先拾取
                print("[Ant-%d]的叶子被别的蚂蚁抢先拾取...." % obj.id)
                obj.state = 1
        else:
            #   当前蚂蚁附近没有叶子了,执行探索
            obj.state = 1

class TransState(State):
    def exec(self, obj):
        leaf_ = obj.microcosm.getObj(obj.leaf_id)
        print("[Ant-%d]拾起叶子,走回巢穴..." % obj.id)
        #   通过蚂蚁当前的位置确定其拾取到的叶子的位置-----》待优化
        leaf_.position = Vector2(obj.position)
        print(leaf_.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
            obj.leaf_id = None
            #   信号置为1,继续探索
            obj.state = 1
        else:
            obj.position += heading
            #   还未将叶子运回巢穴之内,继续trans
            obj.state = 3

class StateMachine(object):
    def __init__(self):
        #   状态集合
        self.states = {0: WaitSate(), 1: ExploreState(), 2: SeekState(), 3: TransState()}

    #   改变状态
    def changeState(self, objs):
        for obj in objs:
            #   这里先留着吧,考虑下state是否有为None的必要
            if obj.state is None:
                return
            else:
                if obj.name == 'leaf':
                    #   无状态,不做处理,其实也可以在外层先过滤掉leaf对象
                    pass
                else:
                    print("name[%s]--state[%d]" % (obj.name, obj.state))
                    newFsm = self.states[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(3):
    ant = Ant(micr, 'ant', r"E:\PycharmProjects\PGStudy\resource\ant.png")
    ant.bind(0, sm.states[0])
    micr.addObj(ant)

while True:
    checkForOut()
    #   生出叶子
    if randint(1, 100) == 1:
        leaf = Leaf(micr, 'leaf', r"E:\PycharmProjects\PGStudy\resource\leaf.png")
        leaf.bind(0, sm.states[0])
        micr.addObj(leaf)
    #   将绘制方法全部封装,每次循环重刷屏幕上所有对象
    micr.draw(screen)
    sm.changeState(micr.capricious_list)
    pygame.display.update()
    pygame.time.delay(10)

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值