经历了长年的艰苦卓绝的披星戴月的惨绝人寰的跋山涉水,我们终于接近了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()

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

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

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

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

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

本次使用的图像资源:

叶子:leaf.png
蚂蚁:ant.png
蜘蛛:spider.png

用Python和Pygame写游戏-从入门到精通(16)相关推荐

  1. python教程是用什么博客写的-用Python和Pygame写游戏-从入门到精通(目录)

    目光博客一开始,就有一个将pygame好好介绍一遍的宏伟计划,历时四个月,在各位朋友的关怀鞭策下,如今(2011/8/26)理论学习的部分似乎已经都完成了,在次列一个目录,方便查询.介绍还不是很全,下 ...

  2. 用Python和Pygame写游戏-从入门到精通(1)

    From: http://eyehere.net/2011/python-pygame-novice-professional-1/ 博客刚开,打算做一个Pygame的系列,翻译自Will McGug ...

  3. python游戏开发框架_用Python和Pygame写游戏-从入门到精通(实战二:恶搞俄罗斯方块1)...

    游戏是为了什么而存在的?Bingo,是为了娱乐~ 在这个最高主题之前,技术啥的什么都无所谓! 前一段时间,有位姓刘的网友用Pygame写了个俄罗斯方块,在用py2exe打包的时候遇到一些问题,和我交流 ...

  4. 用Python和Pygame写游戏-从入门到精通

    博客刚开,打算做一个Pygame的系列,翻译自Will McGugan的<Beginning Game Development with Python and Pygame –From Novi ...

  5. python用户界面游戏_用Python和Pygame写游戏-从入门到精通(实战二:恶搞俄罗斯方块2)...

    我们接着来做这个整死人不偿命的俄罗斯方块. 代码组织和名词约定 上一次我们稍微整理了一下游戏运行的框架,这里需要整理一下python代码的框架,一个典型的pygame脚本结构如下: 其中,lib为py ...

  6. 用python和pygame写游戏_用Python和Pygame写游戏-从入门到精通(6)

    掌握了小小的像素,我们可以使用更加复杂一点的东西了,对,就是图像,无数的像素的集合~还记得上次我们为了生成的一张图片,花了无数时间,还好一般游戏不会在游戏的过程中动态生成图像,都是将画好的作为资源封装 ...

  7. python游戏代码示例_用Python和Pygame写游戏-从入门到精通(Sprite篇)

    这又是Pygame教程系列的一个--OVA篇,类似于py2exe篇一样,额外写的,也许不是pygame游戏开发必须的东西,但是知道了绝对大有裨益.因此友情大放送~ 看pygame模块介绍的时候,细心的 ...

  8. 用Python和Pygame写游戏-从入门到精通(6)学习笔记

    虽然是基础,这里还是要罗嗦一下,之前说的RBG图像,在游戏中我们往往使用RGBA图像,这个A是alpha,也就是表示透明度的部分,值也是0~255,0代表完全透明,255是完全不透明,而像100这样的 ...

  9. python编写赛车游戏单机版_用Python和Pygame写游戏-从入门到精通(20)

    声音是游戏中必要的元素之一,音效可以给予用户良好的反馈体验.赛车的时候可以听到振奋人心的启动时的引擎声和刹车时轮胎摩擦声,射击游戏中枪支弹药的音效和呐喊助威的嗓音,无一不是让人热血沸腾的要因. 宛若电 ...

最新文章

  1. 斥资2亿加码新消费,“瓜子之王”洽洽要圆“坚果梦”?
  2. BCB 串口控件的使用 TComm
  3. python爬取岗位数据并分析_区块链岗位薪资高,Python爬取300个区块链岗位分析,龙虎榜出炉...
  4. 素数方阵(信息学奥赛一本通-T1446)
  5. 改善深层神经网络:超参数调整、正则化以及优化——2.7 RMSprop
  6. mysql 从库relay_MySQL主库binlog(master-log)与从库relay-log关系代码详解
  7. 二级VB培训笔记10:知识点串讲
  8. 95-40-032-java.util.concurrent-ConcurrentHashMap
  9. java BufferedReader
  10. 7-7 评分规则 (5 分)
  11. gis重分类失败可能原因
  12. APP专项测试方法和工具的使用
  13. MYSQL 命令行大全
  14. 1微秒等于多少皮秒_秒的换算:皮秒、纳秒、微秒、毫秒
  15. 客服对于Kindle电子书的退货、倒闭、VR等问题的回答
  16. 利用JNative调用MediaInfo.dll
  17. python基础-07-元组/字典的常用基本操作
  18. kubernetes源码剖析读后感(一)
  19. Beautiful Soup的使用
  20. PMP备考指南之第三章:项目经理的角色

热门文章

  1. html个人简历/form表单提交版
  2. 使用python进行“中文词频分析”学习笔记
  3. 实名认证接口 实名制API
  4. 年终总结——驻足赏风景
  5. 淡然而不漠然,幸福才不会擦肩!
  6. STM32F407串口
  7. 通过网页api接口获取网页数据
  8. 新手入门 kali安装vmtools的万能方法
  9. C# 中的Tag属性
  10. Android服务之Service(四)--ASDL传递复杂数据对象