本文转自:http://eyehere.net/2011/python-pygame-novice-professional-16/

经历了长年的艰苦卓绝的披星戴月的惨绝人寰的跋山涉水,我们终于接近了AI之旅的尾声(好吧,实际上我们这才是刚刚开始)。这一次真正展示一下这几回辛勤工作的结果,最后的画面会是这个样子:

下面给出完整代码(注意需要gameobjects库才可以运行,参考之前的向量篇):

  1. SCREEN_SIZE = (640, 480)
  2. NEST_POSITION = (320, 240)
  3. ANT_COUNT = 20
  4. NEST_SIZE = 100.
  5. import pygame
  6. from pygame.locals import *
  7. from random import randint, choice
  8. from gameobjects.vector2 import Vector2
  9. class State(object):
  10. def __init__(self, name):
  11. self.name = name
  12. def do_actions(self):
  13. pass
  14. def check_conditions(self):
  15. pass
  16. def entry_actions(self):
  17. pass
  18. def exit_actions(self):
  19. pass
  20. class StateMachine(object):
  21. def __init__(self):
  22. self.states = {}
  23. self.active_state = None
  24. def add_state(self, state):
  25. self.states[state.name] = state
  26. def think(self):
  27. if self.active_state is None:
  28. return
  29. self.active_state.do_actions()
  30. new_state_name = self.active_state.check_conditions()
  31. if new_state_name is not None:
  32. self.set_state(new_state_name)
  33. def set_state(self, new_state_name):
  34. if self.active_state is not None:
  35. self.active_state.exit_actions()
  36. self.active_state = self.states[new_state_name]
  37. self.active_state.entry_actions()
  38. class World(object):
  39. def __init__(self):
  40. self.entities = {}
  41. self.entity_id = 0
  42. self.background = pygame.surface.Surface(SCREEN_SIZE).convert()
  43. self.background.fill((255, 255, 255))
  44. pygame.draw.circle(self.background, (200, 255, 200), NEST_POSITION, int(NEST_SIZE))
  45. def add_entity(self, entity):
  46. self.entities[self.entity_id] = entity
  47. entity.id = self.entity_id
  48. self.entity_id += 1
  49. def remove_entity(self, entity):
  50. del self.entities[entity.id]
  51. def get(self, entity_id):
  52. if entity_id in self.entities:
  53. return self.entities[entity_id]
  54. else:
  55. return None
  56. def process(self, time_passed):
  57. time_passed_seconds = time_passed / 1000.0
  58. for entity in self.entities.values():
  59. entity.process(time_passed_seconds)
  60. def render(self, surface):
  61. surface.blit(self.background, (0, 0))
  62. for entity in self.entities.itervalues():
  63. entity.render(surface)
  64. def get_close_entity(self, name, location, range=100.):
  65. location = Vector2(*location)
  66. for entity in self.entities.itervalues():
  67. if entity.name == name:
  68. distance = location.get_distance_to(entity.location)
  69. if distance < range:
  70. return entity
  71. return None
  72. class GameEntity(object):
  73. def __init__(self, world, name, image):
  74. self.world = world
  75. self.name = name
  76. self.image = image
  77. self.location = Vector2(0, 0)
  78. self.destination = Vector2(0, 0)
  79. self.speed = 0.
  80. self.brain = StateMachine()
  81. self.id = 0
  82. def render(self, surface):
  83. x, y = self.location
  84. w, h = self.image.get_size()
  85. surface.blit(self.image, (x-w/2, y-h/2))
  86. def process(self, time_passed):
  87. self.brain.think()
  88. if self.speed > 0. and self.location != self.destination:
  89. vec_to_destination = self.destination - self.location
  90. distance_to_destination = vec_to_destination.get_length()
  91. heading = vec_to_destination.get_normalized()
  92. travel_distance = min(distance_to_destination, time_passed * self.speed)
  93. self.location += travel_distance * heading
  94. class Leaf(GameEntity):
  95. def __init__(self, world, image):
  96. GameEntity.__init__(self, world, “leaf”, image)
  97. class Spider(GameEntity):
  98. def __init__(self, world, image):
  99. GameEntity.__init__(self, world, “spider”, image)
  100. self.dead_image = pygame.transform.flip(image, 0, 1)
  101. self.health = 25
  102. self.speed = 50. + randint(-20, 20)
  103. def bitten(self):
  104. self.health -= 1
  105. if self.health <= 0:
  106. self.speed = 0.
  107. self.image = self.dead_image
  108. self.speed = 140.
  109. def render(self, surface):
  110. GameEntity.render(self, surface)
  111. x, y = self.location
  112. w, h = self.image.get_size()
  113. bar_x = x - 12
  114. bar_y = y + h/2
  115. surface.fill( (255, 0, 0), (bar_x, bar_y, 25, 4))
  116. surface.fill( (0, 255, 0), (bar_x, bar_y, self.health, 4))
  117. def process(self, time_passed):
  118. x, y = self.location
  119. if x > SCREEN_SIZE[0] + 2:
  120. self.world.remove_entity(self)
  121. return
  122. GameEntity.process(self, time_passed)
  123. class Ant(GameEntity):
  124. def __init__(self, world, image):
  125. GameEntity.__init__(self, world, “ant”, image)
  126. exploring_state = AntStateExploring(self)
  127. seeking_state = AntStateSeeking(self)
  128. delivering_state = AntStateDelivering(self)
  129. hunting_state = AntStateHunting(self)
  130. self.brain.add_state(exploring_state)
  131. self.brain.add_state(seeking_state)
  132. self.brain.add_state(delivering_state)
  133. self.brain.add_state(hunting_state)
  134. self.carry_image = None
  135. def carry(self, image):
  136. self.carry_image = image
  137. def drop(self, surface):
  138. if self.carry_image:
  139. x, y = self.location
  140. w, h = self.carry_image.get_size()
  141. surface.blit(self.carry_image, (x-w, y-h/2))
  142. self.carry_image = None
  143. def render(self, surface):
  144. GameEntity.render(self, surface)
  145. if self.carry_image:
  146. x, y = self.location
  147. w, h = self.carry_image.get_size()
  148. surface.blit(self.carry_image, (x-w, y-h/2))
  149. class AntStateExploring(State):
  150. def __init__(self, ant):
  151. State.__init__(self, “exploring”)
  152. self.ant = ant
  153. def random_destination(self):
  154. w, h = SCREEN_SIZE
  155. self.ant.destination = Vector2(randint(0, w), randint(0, h))
  156. def do_actions(self):
  157. if randint(1, 20) == 1:
  158. self.random_destination()
  159. def check_conditions(self):
  160. leaf = self.ant.world.get_close_entity(“leaf”, self.ant.location)
  161. if leaf is not None:
  162. self.ant.leaf_id = leaf.id
  163. return “seeking”
  164. spider = self.ant.world.get_close_entity(“spider”, NEST_POSITION, NEST_SIZE)
  165. if spider is not None:
  166. if self.ant.location.get_distance_to(spider.location) < 100.:
  167. self.ant.spider_id = spider.id
  168. return “hunting”
  169. return None
  170. def entry_actions(self):
  171. self.ant.speed = 120. + randint(-30, 30)
  172. self.random_destination()
  173. class AntStateSeeking(State):
  174. def __init__(self, ant):
  175. State.__init__(self, “seeking”)
  176. self.ant = ant
  177. self.leaf_id = None
  178. def check_conditions(self):
  179. leaf = self.ant.world.get(self.ant.leaf_id)
  180. if leaf is None:
  181. return “exploring”
  182. if self.ant.location.get_distance_to(leaf.location) < 5.0:
  183. self.ant.carry(leaf.image)
  184. self.ant.world.remove_entity(leaf)
  185. return “delivering”
  186. return None
  187. def entry_actions(self):
  188. leaf = self.ant.world.get(self.ant.leaf_id)
  189. if leaf is not None:
  190. self.ant.destination = leaf.location
  191. self.ant.speed = 160. + randint(-20, 20)
  192. class AntStateDelivering(State):
  193. def __init__(self, ant):
  194. State.__init__(self, “delivering”)
  195. self.ant = ant
  196. def check_conditions(self):
  197. if Vector2(*NEST_POSITION).get_distance_to(self.ant.location) < NEST_SIZE:
  198. if (randint(1, 10) == 1):
  199. self.ant.drop(self.ant.world.background)
  200. return “exploring”
  201. return None
  202. def entry_actions(self):
  203. self.ant.speed = 60.
  204. random_offset = Vector2(randint(-20, 20), randint(-20, 20))
  205. self.ant.destination = Vector2(*NEST_POSITION) + random_offset
  206. class AntStateHunting(State):
  207. def __init__(self, ant):
  208. State.__init__(self, “hunting”)
  209. self.ant = ant
  210. self.got_kill = False
  211. def do_actions(self):
  212. spider = self.ant.world.get(self.ant.spider_id)
  213. if spider is None:
  214. return
  215. self.ant.destination = spider.location
  216. if self.ant.location.get_distance_to(spider.location) < 15.:
  217. if randint(1, 5) == 1:
  218. spider.bitten()
  219. if spider.health <= 0:
  220. self.ant.carry(spider.image)
  221. self.ant.world.remove_entity(spider)
  222. self.got_kill = True
  223. def check_conditions(self):
  224. if self.got_kill:
  225. return “delivering”
  226. spider = self.ant.world.get(self.ant.spider_id)
  227. if spider is None:
  228. return “exploring”
  229. if spider.location.get_distance_to(NEST_POSITION) > NEST_SIZE * 3:
  230. return “exploring”
  231. return None
  232. def entry_actions(self):
  233. self.speed = 160. + randint(0, 50)
  234. def exit_actions(self):
  235. self.got_kill = False
  236. def run():
  237. pygame.init()
  238. screen = pygame.display.set_mode(SCREEN_SIZE, 0, 32)
  239. world = World()
  240. w, h = SCREEN_SIZE
  241. clock = pygame.time.Clock()
  242. ant_image = pygame.image.load(“ant.png”).convert_alpha()
  243. leaf_image = pygame.image.load(“leaf.png”).convert_alpha()
  244. spider_image = pygame.image.load(“spider.png”).convert_alpha()
  245. for ant_no in xrange(ANT_COUNT):
  246. ant = Ant(world, ant_image)
  247. ant.location = Vector2(randint(0, w), randint(0, h))
  248. ant.brain.set_state(“exploring”)
  249. world.add_entity(ant)
  250. while True:
  251. for event in pygame.event.get():
  252. if event.type == QUIT:
  253. return
  254. time_passed = clock.tick(30)
  255. if randint(1, 10) == 1:
  256. leaf = Leaf(world, leaf_image)
  257. leaf.location = Vector2(randint(0, w), randint(0, h))
  258. world.add_entity(leaf)
  259. if randint(1, 100) == 1:
  260. spider = Spider(world, spider_image)
  261. spider.location = Vector2(-50, randint(0, h))
  262. spider.destination = Vector2(w+50, randint(0, h))
  263. world.add_entity(spider)
  264. world.process(time_passed)
  265. world.render(screen)
  266. pygame.display.update()
  267. if __name__ == “__main__”:
  268. run()

这个程序的长度超过了以往任何一个,甚至可能比我们写的加起来都要长一些。然而它可以展现给我们的也前所未有的惊喜。无数勤劳的小蚂蚁在整个地图上到处觅食,随机出现的叶子一旦被蚂蚁发现,就会搬回巢穴,而蜘蛛一旦出现在巢穴范围之内,就会被蚂蚁们群起而攻之,直到被驱逐出地图范围或者挂了,蜘蛛的尸体也会被带入巢穴。

这个代码写的不够漂亮,没有用太高级的语法,甚至都没有注释天哪……基本代码都在前面出现了,只是新引入了四个新的状态,AntStateExploring、AntStateSeeking、AntStateDelivering和AntStateHunting,意义的话前面已经说明。比如说AntStateExploring,继承了基本的Stat,这个状态的动作平时就是让蚂蚁以一个随机的速度走向屏幕随机一个点,在此过程中,check_conditions会不断检查周围的环境,发现了树叶或蜘蛛都会采取相应的措施(进入另外一个状态)。

游戏设计艺术中,创建一个漂亮的AI是非常有挑战性也非常有趣的事情。好的AI能让玩家沉浸其中,而糟糕的AI则让人感到非常乏味(有的时候AI中的一些bug被当作秘籍使用,也挺有意思的,不过如果到处是“秘籍”,可就惨了)。而且,AI是否足够聪明有时候并不与代码量直接相关,看看我们这个演示,感觉上去蚂蚁会合作攻击蜘蛛,而实际上它们都是独立行动的,不过就结果而言蚂蚁们看起来都很聪明。

对AI而已,状态机是个很有力的工具(当然状态机不仅仅用在这里),因为状态机可以把复杂的系统分割成几个容易实现的小段。而这每一小部分都是对一些简单思考或动作的模拟,即便不是那么容易转化为代码,也很容易模拟。在游戏中,我们只需要模拟就足够了。

我们这几次讲述的东西相当有用,尽管不是那么直观,但对于游戏设计至关重要,而此次的蚁巢演示,也给我们揭示了AI系统的种种,至少这个系统式可以运作的了,不错不错~ 参天大树也是从小树苗开始的。

本次使用的图像资源:

叶子:leaf.png

蚂蚁:ant.png
蜘蛛:spider.png

下一次,我们开始更加激动人心的项目,3D图像!

如何用Pygame写游戏(十六)相关推荐

  1. 如何用Pygame写游戏(六)

    本文转自:http://eyehere.net/2011/python-pygame-novice-professional-6/ 掌握了小小的像素,我们可以使用更加复杂一点的东西了,对,就是图像,无 ...

  2. 如何用Pygame写游戏(十)

    本文转自:http://eyehere.net/2011/python-pygame-novice-professional-10/ 有时候无聊在网上翻翻小说看看,绝大多数那叫一个无聊.比如说修炼的境 ...

  3. 如何用Pygame写游戏(一)

    转载地址:http://eyehere.net/2011/python-pygame-novice-professional-1/ Pygame的历史  Pygame是一个利用SDL库的写就的游戏 ...

  4. 如何用Pygame写游戏(二十二)

    本文转自:http://eyehere.net/2011/python-pygame-novice-professional-22/ 辛苦啦~ 这次是我们系统的pygame理论学习的最后一章了,把这次 ...

  5. 如何用Pygame写游戏(十九)

    本文转自:http://eyehere.net/2011/python-pygame-novice-professional-19/ 3D世界 让我们现在开始写一个3D的程序,巩固一下这几次学习的东西 ...

  6. 如何用Pygame写游戏(十八)

    本文转自:http://eyehere.net/2011/python-pygame-novice-professional-18/ 3D是非常酷的技术,同时也就意味着更多的工作,上次的简单介绍之后, ...

  7. 如何用Pygame写游戏(十四)

    本文转自:http://eyehere.net/2011/python-pygame-novice-professional-14/ 上一次稍微说了一下AI,为了更好的理解它,我们必须明白什么是状态机 ...

  8. 如何用Pygame写游戏(二十)

    本文转自:http://eyehere.net/2011/python-pygame-novice-professional-20/ 声音是游戏中必要的元素之一,音效可以给予用户良好的反馈体验.赛车的 ...

  9. 如何用Pygame写游戏(十五)

    本文转自: http://eyehere.net/2011/python-pygame-novice-professional-15/ 在继续我们的AI之旅前,分享一个在煎蛋上看到的有趣新闻,能通过读 ...

最新文章

  1. 计算机aoa综合题word,AOA-word综合题操作步骤(修正版)
  2. 水木-搜索引擎技术版
  3. linux终端显示被覆盖,图文说明:Linux监控命令全覆盖
  4. if you want to go to ruiyuan fund
  5. 如何linux查看mysql目录下日志_测试人员如何在linux服务器中查询mysql日志?
  6. “小会话,大学问” - 如何让聊天机器人读懂对话历史?| 论文访谈间 #03
  7. 深入理解Java:注解(Annotation)
  8. ios系统脚本服务器加速,提高iOS项目的编译速度
  9. swf游戏保存进度_flash格式如何保存为swf格式动画?flash格式小游戏保存成swf格式方法 - 软件教程 - 格子啦...
  10. 工具介绍(3)- TS 视频文件分析工具神器
  11. 拼多多网站的服务器多大,拼多多打不开网页怎么回事
  12. 【演讲之路】钱塘TMC互联网思维分享会
  13. 葵花宝典:软件开发高手是这样炼成的!
  14. bzoj 3772: 精神污染 (主席树+dfs序)
  15. 计算机软硬件问题及解决方法(经验篇)
  16. 微信公众号数据2019_2019年微信公众号文章数据报告
  17. volatile不能保证原子性,atomic不仅保证可见性还有原子性CAS分析
  18. urv中保研碰撞测试结果_经撞=安全?中保研碰撞测试结果告诉我们:不一定!|乜都知...
  19. python外星人入侵游戏代码_python外星人入侵 游戏源码
  20. Hive(6):数据定义语言(DDL)案例

热门文章

  1. C语言 strcpy函数实现
  2. try... except异常处理结构
  3. linux交叉编译+驱动,请教驱动程序交叉编译问题(初学)
  4. mysql set substring_MySQL substring()函数
  5. Beyond Compare 出现“这个许可证密钥已被撤销”的解决办法(不会删除记录)
  6. MetaTransformer:简单到尴尬的视觉模型
  7. python网络框架生产环境_配置Django框架为生产环境的注意事项(DEBUG=False)
  8. 神经网络与深度学习 吴恩达 第一课第四周 习题
  9. 快查电脑:开关机记录等(电脑使用痕迹)
  10. python读取xlsx_Python读取xlsx文件的实现方法