用Python和Pygame写游戏-从入门到精通(16)
经历了长年的艰苦卓绝的披星戴月的惨绝人寰的跋山涉水,我们终于接近了AI之旅的尾声(好吧,实际上我们这才是刚刚开始)。这一次真正展示一下这几回辛勤工作的结果,最后的画面会是这个样子:
下面给出完整代码(注意需要gameobjects库才可以运行,参考之前的向量篇):
Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 |
SCREEN_SIZE = (640, 480) NEST_POSITION = (320, 240) ANT_COUNT = 20 NEST_SIZE = 100. import pygame from pygame.locals import * from random import randint, choice from gameobjects.vector2 import Vector2 class State(object): def __init__(self, name): self.name = name def do_actions(self): pass def check_conditions(self): pass def entry_actions(self): pass def exit_actions(self): pass class StateMachine(object): def __init__(self): self.states = {} self.active_state = None def add_state(self, state): self.states[state.name] = state def think(self): if self.active_state is None: return self.active_state.do_actions() new_state_name = self.active_state.check_conditions() if new_state_name is not None: self.set_state(new_state_name) def set_state(self, new_state_name): if self.active_state is not None: self.active_state.exit_actions() self.active_state = self.states[new_state_name] self.active_state.entry_actions() class World(object): def __init__(self): self.entities = {} self.entity_id = 0 self.background = pygame.surface.Surface(SCREEN_SIZE).convert() self.background.fill((255, 255, 255)) pygame.draw.circle(self.background, (200, 255, 200), NEST_POSITION, int(NEST_SIZE)) def add_entity(self, entity): self.entities[self.entity_id] = entity entity.id = self.entity_id self.entity_id += 1 def remove_entity(self, entity): del self.entities[entity.id] def get(self, entity_id): if entity_id in self.entities: return self.entities[entity_id] else: return None def process(self, time_passed): time_passed_seconds = time_passed / 1000.0 for entity in self.entities.values(): entity.process(time_passed_seconds) def render(self, surface): surface.blit(self.background, (0, 0)) for entity in self.entities.itervalues(): entity.render(surface) def get_close_entity(self, name, location, range=100.): location = Vector2(*location) for entity in self.entities.itervalues(): if entity.name == name: distance = location.get_distance_to(entity.location) if distance < range: return entity return None class GameEntity(object): def __init__(self, world, name, image): self.world = world self.name = name self.image = image self.location = Vector2(0, 0) self.destination = Vector2(0, 0) self.speed = 0. self.brain = StateMachine() self.id = 0 def render(self, surface): x, y = self.location w, h = self.image.get_size() surface.blit(self.image, (x-w/2, y-h/2)) def process(self, time_passed): self.brain.think() if self.speed > 0. and self.location != self.destination: vec_to_destination = self.destination - self.location distance_to_destination = vec_to_destination.get_length() heading = vec_to_destination.get_normalized() travel_distance = min(distance_to_destination, time_passed * self.speed) self.location += travel_distance * heading class Leaf(GameEntity): def __init__(self, world, image): GameEntity.__init__(self, world, "leaf", image) class Spider(GameEntity): def __init__(self, world, image): GameEntity.__init__(self, world, "spider", image) self.dead_image = pygame.transform.flip(image, 0, 1) self.health = 25 self.speed = 50. + randint(-20, 20) def bitten(self): self.health -= 1 if self.health <= 0: self.speed = 0. self.image = self.dead_image self.speed = 140. def render(self, surface): GameEntity.render(self, surface) x, y = self.location w, h = self.image.get_size() bar_x = x - 12 bar_y = y + h/2 surface.fill( (255, 0, 0), (bar_x, bar_y, 25, 4)) surface.fill( (0, 255, 0), (bar_x, bar_y, self.health, 4)) def process(self, time_passed): x, y = self.location if x > SCREEN_SIZE[0] + 2: self.world.remove_entity(self) return GameEntity.process(self, time_passed) class Ant(GameEntity): def __init__(self, world, image): GameEntity.__init__(self, world, "ant", image) exploring_state = AntStateExploring(self) seeking_state = AntStateSeeking(self) delivering_state = AntStateDelivering(self) hunting_state = AntStateHunting(self) self.brain.add_state(exploring_state) self.brain.add_state(seeking_state) self.brain.add_state(delivering_state) self.brain.add_state(hunting_state) self.carry_image = None def carry(self, image): self.carry_image = image def drop(self, surface): if self.carry_image: x, y = self.location w, h = self.carry_image.get_size() surface.blit(self.carry_image, (x-w, y-h/2)) self.carry_image = None def render(self, surface): GameEntity.render(self, surface) if self.carry_image: x, y = self.location w, h = self.carry_image.get_size() surface.blit(self.carry_image, (x-w, y-h/2)) class AntStateExploring(State): def __init__(self, ant): State.__init__(self, "exploring") self.ant = ant def random_destination(self): w, h = SCREEN_SIZE self.ant.destination = Vector2(randint(0, w), randint(0, h)) def do_actions(self): if randint(1, 20) == 1: self.random_destination() def check_conditions(self): leaf = self.ant.world.get_close_entity("leaf", self.ant.location) if leaf is not None: self.ant.leaf_id = leaf.id return "seeking" spider = self.ant.world.get_close_entity("spider", NEST_POSITION, NEST_SIZE) if spider is not None: if self.ant.location.get_distance_to(spider.location) < 100.: self.ant.spider_id = spider.id return "hunting" return None def entry_actions(self): self.ant.speed = 120. + randint(-30, 30) self.random_destination() class AntStateSeeking(State): def __init__(self, ant): State.__init__(self, "seeking") self.ant = ant self.leaf_id = None def check_conditions(self): leaf = self.ant.world.get(self.ant.leaf_id) if leaf is None: return "exploring" if self.ant.location.get_distance_to(leaf.location) < 5.0: self.ant.carry(leaf.image) self.ant.world.remove_entity(leaf) return "delivering" return None def entry_actions(self): leaf = self.ant.world.get(self.ant.leaf_id) if leaf is not None: self.ant.destination = leaf.location self.ant.speed = 160. + randint(-20, 20) class AntStateDelivering(State): def __init__(self, ant): State.__init__(self, "delivering") self.ant = ant def check_conditions(self): if Vector2(*NEST_POSITION).get_distance_to(self.ant.location) < NEST_SIZE: if (randint(1, 10) == 1): self.ant.drop(self.ant.world.background) return "exploring" return None def entry_actions(self): self.ant.speed = 60. random_offset = Vector2(randint(-20, 20), randint(-20, 20)) self.ant.destination = Vector2(*NEST_POSITION) + random_offset class AntStateHunting(State): def __init__(self, ant): State.__init__(self, "hunting") self.ant = ant self.got_kill = False def do_actions(self): spider = self.ant.world.get(self.ant.spider_id) if spider is None: return self.ant.destination = spider.location if self.ant.location.get_distance_to(spider.location) < 15.: if randint(1, 5) == 1: spider.bitten() if spider.health <= 0: self.ant.carry(spider.image) self.ant.world.remove_entity(spider) self.got_kill = True def check_conditions(self): if self.got_kill: return "delivering" spider = self.ant.world.get(self.ant.spider_id) if spider is None: return "exploring" if spider.location.get_distance_to(NEST_POSITION) > NEST_SIZE * 3: return "exploring" return None def entry_actions(self): self.speed = 160. + randint(0, 50) def exit_actions(self): self.got_kill = False def run(): pygame.init() screen = pygame.display.set_mode(SCREEN_SIZE, 0, 32) world = World() w, h = SCREEN_SIZE clock = pygame.time.Clock() ant_image = pygame.image.load("ant.png").convert_alpha() leaf_image = pygame.image.load("leaf.png").convert_alpha() spider_image = pygame.image.load("spider.png").convert_alpha() for ant_no in xrange(ANT_COUNT): ant = Ant(world, ant_image) ant.location = Vector2(randint(0, w), randint(0, h)) ant.brain.set_state("exploring") world.add_entity(ant) while True: for event in pygame.event.get(): if event.type == QUIT: return time_passed = clock.tick(30) if randint(1, 10) == 1: leaf = Leaf(world, leaf_image) leaf.location = Vector2(randint(0, w), randint(0, h)) world.add_entity(leaf) if randint(1, 100) == 1: spider = Spider(world, spider_image) spider.location = Vector2(-50, randint(0, h)) spider.destination = Vector2(w+50, randint(0, h)) world.add_entity(spider) world.process(time_passed) world.render(screen) pygame.display.update() if __name__ == "__main__": run() |
这个程序的长度超过了以往任何一个,甚至可能比我们写的加起来都要长一些。然而它可以展现给我们的也前所未有的惊喜。无数勤劳的小蚂蚁在整个地图上到处觅食,随机出现的叶子一旦被蚂蚁发现,就会搬回巢穴,而蜘蛛一旦出现在巢穴范围之内,就会被蚂蚁们群起而攻之,直到被驱逐出地图范围或者挂了,蜘蛛的尸体也会被带入巢穴。
这个代码写的不够漂亮,没有用太高级的语法,甚至都没有注释天哪……基本代码都在前面出现了,只是新引入了四个新的状态,AntStateExploring、AntStateSeeking、AntStateDelivering和AntStateHunting,意义的话前面已经说明。比如说AntStateExploring,继承了基本的Stat,这个状态的动作平时就是让蚂蚁以一个随机的速度走向屏幕随机一个点,在此过程中,check_conditions会不断检查周围的环境,发现了树叶或蜘蛛都会采取相应的措施(进入另外一个状态)。
游戏设计艺术中,创建一个漂亮的AI是非常有挑战性也非常有趣的事情。好的AI能让玩家沉浸其中,而糟糕的AI则让人感到非常乏味(有的时候AI中的一些bug被当作秘籍使用,也挺有意思的,不过如果到处是“秘籍”,可就惨了)。而且,AI是否足够聪明有时候并不与代码量直接相关,看看我们这个演示,感觉上去蚂蚁会合作攻击蜘蛛,而实际上它们都是独立行动的,不过就结果而言蚂蚁们看起来都很聪明。
对AI而已,状态机是个很有力的工具(当然状态机不仅仅用在这里),因为状态机可以把复杂的系统分割成几个容易实现的小段。而这每一小部分都是对一些简单思考或动作的模拟,即便不是那么容易转化为代码,也很容易模拟。在游戏中,我们只需要模拟就足够了。
我们这几次讲述的东西相当有用,尽管不是那么直观,但对于游戏设计至关重要,而此次的蚁巢演示,也给我们揭示了AI系统的种种,至少这个系统式可以运作的了,不错不错~ 参天大树也是从小树苗开始的。
本次使用的图像资源:
叶子:leaf.png
蚂蚁:ant.png
蜘蛛:spider.png
用Python和Pygame写游戏-从入门到精通(16)相关推荐
- python教程是用什么博客写的-用Python和Pygame写游戏-从入门到精通(目录)
目光博客一开始,就有一个将pygame好好介绍一遍的宏伟计划,历时四个月,在各位朋友的关怀鞭策下,如今(2011/8/26)理论学习的部分似乎已经都完成了,在次列一个目录,方便查询.介绍还不是很全,下 ...
- 用Python和Pygame写游戏-从入门到精通(1)
From: http://eyehere.net/2011/python-pygame-novice-professional-1/ 博客刚开,打算做一个Pygame的系列,翻译自Will McGug ...
- python游戏开发框架_用Python和Pygame写游戏-从入门到精通(实战二:恶搞俄罗斯方块1)...
游戏是为了什么而存在的?Bingo,是为了娱乐~ 在这个最高主题之前,技术啥的什么都无所谓! 前一段时间,有位姓刘的网友用Pygame写了个俄罗斯方块,在用py2exe打包的时候遇到一些问题,和我交流 ...
- 用Python和Pygame写游戏-从入门到精通
博客刚开,打算做一个Pygame的系列,翻译自Will McGugan的<Beginning Game Development with Python and Pygame –From Novi ...
- python用户界面游戏_用Python和Pygame写游戏-从入门到精通(实战二:恶搞俄罗斯方块2)...
我们接着来做这个整死人不偿命的俄罗斯方块. 代码组织和名词约定 上一次我们稍微整理了一下游戏运行的框架,这里需要整理一下python代码的框架,一个典型的pygame脚本结构如下: 其中,lib为py ...
- 用python和pygame写游戏_用Python和Pygame写游戏-从入门到精通(6)
掌握了小小的像素,我们可以使用更加复杂一点的东西了,对,就是图像,无数的像素的集合~还记得上次我们为了生成的一张图片,花了无数时间,还好一般游戏不会在游戏的过程中动态生成图像,都是将画好的作为资源封装 ...
- python游戏代码示例_用Python和Pygame写游戏-从入门到精通(Sprite篇)
这又是Pygame教程系列的一个--OVA篇,类似于py2exe篇一样,额外写的,也许不是pygame游戏开发必须的东西,但是知道了绝对大有裨益.因此友情大放送~ 看pygame模块介绍的时候,细心的 ...
- 用Python和Pygame写游戏-从入门到精通(6)学习笔记
虽然是基础,这里还是要罗嗦一下,之前说的RBG图像,在游戏中我们往往使用RGBA图像,这个A是alpha,也就是表示透明度的部分,值也是0~255,0代表完全透明,255是完全不透明,而像100这样的 ...
- python编写赛车游戏单机版_用Python和Pygame写游戏-从入门到精通(20)
声音是游戏中必要的元素之一,音效可以给予用户良好的反馈体验.赛车的时候可以听到振奋人心的启动时的引擎声和刹车时轮胎摩擦声,射击游戏中枪支弹药的音效和呐喊助威的嗓音,无一不是让人热血沸腾的要因. 宛若电 ...
最新文章
- 斥资2亿加码新消费,“瓜子之王”洽洽要圆“坚果梦”?
- BCB 串口控件的使用 TComm
- python爬取岗位数据并分析_区块链岗位薪资高,Python爬取300个区块链岗位分析,龙虎榜出炉...
- 素数方阵(信息学奥赛一本通-T1446)
- 改善深层神经网络:超参数调整、正则化以及优化——2.7 RMSprop
- mysql 从库relay_MySQL主库binlog(master-log)与从库relay-log关系代码详解
- 二级VB培训笔记10:知识点串讲
- 95-40-032-java.util.concurrent-ConcurrentHashMap
- java BufferedReader
- 7-7 评分规则 (5 分)
- gis重分类失败可能原因
- APP专项测试方法和工具的使用
- MYSQL 命令行大全
- 1微秒等于多少皮秒_秒的换算:皮秒、纳秒、微秒、毫秒
- 客服对于Kindle电子书的退货、倒闭、VR等问题的回答
- 利用JNative调用MediaInfo.dll
- python基础-07-元组/字典的常用基本操作
- kubernetes源码剖析读后感(一)
- Beautiful Soup的使用
- PMP备考指南之第三章:项目经理的角色