如何用python 创建用户创建做拼图游戏

用Python设计一个经典小游戏
作者:昊羲
字体:[ ] 类型:转载 时间:
本篇文章主要介绍如何用Python设计一个经典小游戏:猜大小。具有很好的参考价值。下面跟着小编一起来看下吧
本文主要介绍如何用Python设计一个经典小游戏:猜大小。
在这个游戏中,将用到前面我介绍过的所有内容:变量的使用、参数传递、函数设计、条件控制和循环等,做个整体的总结和复习。
游戏规则:
初始本金是1000元,默认赔率是1倍,赢了,获得一倍金额,输了,扣除1倍金额。
玩家选择下注,押大或押小;
输入下注金额;
摇3个骰子,11≤骰子总数≤18为大,3≤骰子总数≤10为小;
如果赢了,获得1倍金额,输了,扣除1倍金额,本金为0时,游戏结束。
程序运行结果是这样的:
现在,我们来梳理下思路。
我们先让程序知道如何摇骰子;
让程序知道什么是大,什么是小;
用户开始玩游戏,如果猜对,赢钱;猜错,输钱;输完后,游戏结束。
梳理清楚思路后,接下来开始敲代码。
定义roll_dice函数,3个骰子,循环次数numbers为3,骰子点数points初始值为空,这里的参数传递用到的是之前讲到的关键词参数传递。
随机数生成用import random来实现。Python中最方便的就是有很多强大的库支持,现在我们可以直接导入一个random的内置库,用它来生成随机数。如:
1 import random
2 point = random.randrange(1,7)
3 # random.randrange(1,7)生成1-6的随机数
4 print(point)
print(point)后可以看到打印出的随机数,每次运行结果都是随机的。
接下来我们看下摇骰子这部分的完整代码:
import random
def roll_dice(numbers = 3,points = None):
print('----- 摇骰子 -----')
if points is None:
points = []
# points为空列表,后续可以插入新值到该列表
while numbers & 0:
point = random.randrange(1,7)
points.append(point)
# 用append()方法将point数值插入points列表中
numbers = numbers - 1
# 完成一次,numbers减1,当小于等于0时不再执行该循环
return points
11≤骰子总数≤18为大,3≤骰子总数≤10为小,代码如下:
def roll_result(total):
isBig = 11 &= total &=18
isSmall = 3 &= total &= 10
return '大'
elif isSmall:
return '小'
初始本金1000元,默认赔率1倍;赢了,获得一倍金额,输了,扣除1倍金额;本金为0时,游戏结束。
def start_game():
your_money = 1000
while your_money & 0:
print('----- 游戏开始 -----')
choices = ['大','小']
# choices赋值为大和小,用户需输入二者之一为正确
your_choice = input('请下注,大 or 小:')
your_bet = input('下注金额:')
if your_choice in choices:
points = roll_dice()
# 调用roll_dice函数
total = sum(points)
# sum为相加,将3个骰子的结果相加
youWin = your_choice == roll_result(total)
if youWin:
print('骰子点数:',points)
print('恭喜,你赢了 {} 元,你现在有 {} 元本金'.format(your_bet,your_money + int(your_bet)))
# your_bet是字符串格式,这里需要转化为int类型进行计算
your_money = your_money + int(your_bet)
# 最新本金
print('骰子点数:',points)
print('很遗憾,你输了 {} 元,你现在有 {} 元本金'.format(your_bet, your_money - int(your_bet)))
your_money = your_money - int(your_bet)
print('格式有误,请重新输入')
# 如果输入的不是choices列表中的大或小,则为格式有误
print('游戏结束')
start_game()
到这里,我们就完成了该游戏三大部分的设计,大家一定要仔细思考,梳理设计思路,动手敲出代码才好。
最后,附【猜大小】游戏的完整代码:
import random
def roll_dice(numbers = 3,points = None):
print('----- 摇骰子 -----')
if points is None:
points = []
while numbers & 0:
point = random.randrange(1,7)
points.append(point)
numbers = numbers - 1
return points
def roll_result(total):
isBig = 11 &= total &=18
isSmall = 3 &= total &= 10
return '大'
elif isSmall:
return '小'
def start_game():
your_money = 1000
while your_money & 0:
print('----- 游戏开始 -----')
choices = ['大','小']
your_choice = input('请下注,大 or 小:')
your_bet = input('下注金额:')
if your_choice in choices:
points = roll_dice()
total = sum(points)
youWin = your_choice == roll_result(total)
if youWin:
print('骰子点数:',points)
print('恭喜,你赢了 {} 元,你现在有 {} 元本金'.format(your_bet,your_money + int(your_bet)))
your_money = your_money + int(your_bet)
print('骰子点数:',points)
print('很遗憾,你输了 {} 元,你现在有 {} 元本金'.format(your_bet, your_money - int(your_bet)))
your_money = your_money - int(your_bet)
print('格式有误,请重新输入')
print('游戏结束')
start_game()
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持脚本之家!
您可能感兴趣的文章:
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具1117人阅读
cocos2dx-lua(20)
1、创建拼图所需的背景图。
self.fragment_sprite_bg = display.newSprite(self.genie_sprite)
:setAnchorPoint(0, 0)
:addTo(self)
self.content_size = self.fragment_sprite_bg:getContentSize()
self.fragment_sprite_bg:setPosition(display.cx - self.content_size.width/2, display.cy - self.content_size.height/2)
self.fragment_sprite_bg:setOpacity(128)
--背景图透明度设置为半透
2、给背景图添加九宫格线,方便拼图。
local pos_x, pos_y = self.fragment_sprite_bg:getPosition()
local line_1 = cc.DrawNode:create():drawSegment(
cc.p(pos_x, pos_y + self.content_size.height/3), cc.p(pos_x + self.content_size.width, pos_y + self.content_size.height/3), PiecePuzzleGameView.LINE_WIDTH, cc.c4f(0,0,0,1))
local line_2 = cc.DrawNode:create():drawSegment(
cc.p(pos_x, pos_y + 2*self.content_size.height/3), cc.p(pos_x + self.content_size.width, pos_y + 2*self.content_size.height/3), PiecePuzzleGameView.LINE_WIDTH, cc.c4f(0,0,0,1))
local line_3 = cc.DrawNode:create():drawSegment(
cc.p(pos_x + self.content_size.width/3, pos_y), cc.p(pos_x + self.content_size.width/3, pos_y + self.content_size.height), PiecePuzzleGameView.LINE_WIDTH, cc.c4f(0,0,0,1))
local line_4 = cc.DrawNode:create():drawSegment(
cc.p(pos_x + 2*self.content_size.width/3, pos_y), cc.p(pos_x + 2*self.content_size.width/3, pos_y + self.content_size.height), PiecePuzzleGameView.LINE_WIDTH, cc.c4f(0,0,0,1))
self:addChild(line_1)
self:addChild(line_2)
self:addChild(line_3)
self:addChild(line_4)
3、创建拼图图块
通过clippingnode创建。
local fragment_sprite = display.newSprite(self.genie_sprite)
fragment_sprite:setAnchorPoint(0, 0)
local rect = cc.rect(0, 0, self.content_size.width/3, self.content_size.height/3)
--创建一个裁剪区域用于裁剪图块
local clipnode = cc.ClippingRegionNode:create()
clipnode:setClippingRegion(rect)--设置裁剪区域的大小
clipnode:setContentSize(self.content_size.width/3, self.content_size.height/3)
clipnode:addChild(fragment_sprite)--添加图片
fragment_sprite:setPosition(0 - (j-1)*self.content_size.width/3, 0 - (i-1)*self.content_size.height/3)--设置图片显示的部分
self:addChild(clipnode)
self.fragment_table[#self.fragment_table + 1] = clipnode
clipnode:setPosition(pos_x + (j-1)*self.content_size.width/3, pos_y + (i-1)*self.content_size.height/3)
通过sprite创建
local pos_x, pos_y = self.fragment_sprite_bg:getPosition()
local cache = cc.Director:getInstance():getTextureCache():addImage(self.genie_sprite)
local content_size = cache:getContentSize()
for i = 1, 3 do
for j = 1, 3 do
local sprite = cc.Sprite:create()
sprite:setAnchorPoint(0, 0)
sprite:setTexture(cache)
sprite:setTextureRect(cc.rect((i-1)*content_size.width/3, (j-1)*content_size.height/3, content_size.width/3, content_size.height/3))
self:addChild(sprite)
self.fragment_table[
sprite:setPosition(pos_x + (j-1)*self.content_size.width/3, pos_y + (i-1)*self.content_size.height/3)
4、给拼图图块添加拖动事件
clipnode:setTouchEnabled(true)
clipnode:addNodeEventListener(cc.NODE_TOUCH_EVENT, function (event)
--local boundingBox = clipnode:getCascadeBoundingBox()
local position = cc.p(clipnode:getPosition())
local boundingBox = cc.rect(position.x, position.y, self.content_size.width/3, self.content_size.height/3) --getCascadeBoundingBox()方法获得的rect大小为整张图片的大小,此处重新计算图块的rect。
if "began" == event.name and not cc.rectContainsPoint(boundingBox, cc.p(event.x, event.y)) then
clipnode:setTouchSwallowEnabled(false)
return false
if "began" == event.name then
clipnode:setTouchSwallowEnabled(true)--吞噬触摸,防止响应下层的图块。
--将当前的图块置顶
for index = 1, self.fragment_num do
self.fragment_table[index]:setLocalZOrder(PiecePuzzleGameView.FRAGMENT_ZORDER)
clipnode:setLocalZOrder(PiecePuzzleGameView.FRAGMENT_ZORDER + 1)
return true
elseif "moved" == event.name then
local pos_x, pos_y = clipnode:getPosition()
pos_x = pos_x + event.x - event.prevX
pos_y = pos_y + event.y - event.prevY
if pos_x & display.left or pos_x & display.right - self.content_size.width/3 then
pos_x = pos_x - event.x + event.prevX
if pos_y & display.bottom or pos_y & display.top - self.content_size.height/3 then
pos_y = pos_y - event.y + event.prevY
clipnode:setPosition(pos_x, pos_y)
elseif "ended" == event.name then
5、检测图块位置
在拖动事件返回的时候检测图块位置是否在对应位置区域范围内,若在,则将图块放在对应位置上。同时检测是否所有图块均放在了对应位置上,若是,则游戏完成。
local fragment_bg_pos = cc.p(self.fragment_sprite_bg:getPosition())
local pos_x, pos_y = clipnode:getPosition()
if pos_x & fragment_bg_pos.x + (j-1)*self.content_size.width/3 - PiecePuzzleGameView.ERROR_DIS and
pos_x & fragment_bg_pos.x + (j-1)*self.content_size.width/3 + PiecePuzzleGameView.ERROR_DIS and
pos_y & fragment_bg_pos.y + (i-1)*self.content_size.height/3 - PiecePuzzleGameView.ERROR_DIS and
pos_y & fragment_bg_pos.y + (i-1)*self.content_size.height/3 + PiecePuzzleGameView.ERROR_DIS
clipnode:setPosition(fragment_bg_pos.x + (j-1)*self.content_size.width/3, fragment_bg_pos.y + (i-1)*self.content_size.height/3)
clipnode:setTouchEnabled(false) --图块已经放置在了正确位置,就设置为不可点击了。
self.fixed_fragment_num = self.fixed_fragment_num + 1
if self.fixed_fragment_num == self.fragment_num then
self:gameResult(true)
6、随机初始化图块的位置
--随机图块位置
for i = 1, self.fragment_num do
self.fragment_rand_pos[
for i = 1, self.fragment_num do
self.fragment_table[i]:runAction(cc.MoveTo:create(0.2, cc.p(self.fragment_rand_pos[i].x, self.fragment_rand_pos[i].y)))
游戏完成啦啦啦~~~
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:22050次
排名:千里之外
原创:35篇
转载:10篇
(1)(1)(1)(1)(2)(3)(6)(1)(1)(4)(9)(18)(1)后使用快捷导航没有帐号?
查看: 29100|回复: 60
新人欢迎积分1 阅读权限40积分335精华0UID帖子金钱3137 威望0
Lv.4, 积分 335, 距离下一级还需 665 积分
UID帖子威望0 多玩草50 草
——其实是一个失败的输入法候选框
最近在学python语言,但是一直没有什么实践的机会。正好我唯一在玩的网络游戏坦克世界就是用python做脚本语言的,便一拍脑门,便决定把那个蛋疼的输入法问题(打字杀永远的痛)解决掉,于是一发不可收拾。事先透露一下,原本的方案可以说算是流产了……' i3 `9 f% H5 [
$ y- q3 ~; C( \& M& G
但是我这么有节操的人怎么能做无用功呢。自己研究期间搜到多玩坦克世界插件版的帖子基本都是在涂装和图标、语音等方向徘徊,觉得总是比毛服欧服论坛差点什么。为了改善这种状况,我决定拿这次案例来做个脚本方面的教程,权当抛砖引玉,引诱大神们出来打脸,打脸之余顺便提高坛子里关于脚本方面的教程帖子的质量。
0 U+ a. _8 r: I
&纯技术贴啊,楼主乃真大神也!&
&点赞~~友情提示:二楼正文开始~~&
&好好好好好好&
&快来我的群!这种人才我才不会放过!!!!&
你的贴子很不错。推荐一下! O(∩_∩)O.
感谢你帮助玩友O(∩_∩)O
总评分:&金钱 + 323&
多玩草 + 50&
3年单野路过
新人欢迎积分1 阅读权限40积分335精华0UID帖子金钱3137 威望0
Lv.4, 积分 335, 距离下一级还需 665 积分
UID帖子威望0 多玩草50 草
配置篇:python环境& & 这个是必须的啦,诸君看到的所谓pyc后缀名的文件便是py脚本编译后的东西。Windows上的安装方法也是超级简单,设置一下安装路径,一路下一步就ok。实在不明白的可以网上搜一下,很多教程。我这里贴一个图文并茂的:/article/1a62e53e5707e0.html。要注意的是python有两个阵营——python2和python3,坦克世界以前是2.6版本的python,最近好像是换成了2.7.3,大家网上下安装包的时候不要下python3的版本,安全起见,我贴一个下载地址(大部分下载地址都是我的百度网盘,比较容易得到的软件我就不上传了):/s/1eQw3Ssi。至今还在用xp系统的筒子们可以去官网下32位版本的,我就不吐槽了。呃还有一点是python的安装路径不要有中文,虽然不是必须的,但毕竟是开源的东西,一些个人写的包可能会有不识别中文的问题,会出现一些费解的小bug。安装好之后打开控制台(快捷键Windows键+R,然后输入cmd),输入python,出现python的版本、许可信息等,说明你的python已经成功安装上了。
17:30:22 上传
( r- x/ e" q# x5 N9 a
还有一步就是把咱的安装路径填到系统变量中,至于环境变量是个啥,简单来说就是默认路径啦,计算机搜索可执行程序或是bat文件、dll文件之类文件时,优先搜索这些用“;”隔开的一个个路径。我们再编写一个小文件【compile27.py】,来方便我们将以后编写的py文件编译成pyc文件。在安装路径下新建一个Scripts文件夹,
17:33:05 上传
2 z& v7 r0 x1 q1 E5 h& ^
+ w+ y' Z6 A4 _$ Y& P, Y
将这个Scripts文件夹也添加到环境变量中,注意路径之间用分号隔开“;”,把这个compile27.py文件放进去。具体代码如下:
#!usr/bin/env python27
# -*- coding: UTF-8 -*-
% Q+ Q2 I2 ?* P6 m+ q
import sys, py_compile#导入所需要的包9 H4 l5 s1 Q3 h! k- T; `&&R
if len(sys.argv) & 1:#如果输入的参数个数大于1。说明一下,第一个参数是这个文件的文件名,所以如果输入有效的话,系统参数应该是2个以上的,第二个以后就是要编译的py文件了。
& & for argv_i in sys.argv[1:]:#对每个参数,除去第一个本文件名. K0 I, W7 b# v, G+ |- E& L
& && &&&try:#尝试/ N; h5 ^# m& h9 `0 ^% j& \$ f
& && && && &pile(argv_i)#编译py文件为pyc文件, D2 A/ K/ z&&|+ Q7 a: b% D1 Q
& && &&&except:#出错的处理% }' n) V3 n1 `; Z' T
& && && && &print argv_i, &can't be compiled&#在控制台上打印错误消息, q' N/ a" F& ]% Y- \&&j: H& k& Z
else:#如果输入的参数只有一个
& & print &please input the file to be compiled&#在控制台上打印消息,提示输入正确的格式 复制代码& & 没错,这便是筒子们的第一个py脚本了。如果出错的话,删掉#连同后边的中文注释……
【EasyPythonDecompiler】反编译pyc为py文件的工具& & 这个用法也很简单,打开exe后,两个大的选项,
17:37:23 上传
【Decompile a File】是反编译单个pyc文件,【Decompile a Directory】是反编译一个文件夹内的所有pyc文件。有了它之后,咱就可以把坦克世界的整个客户端翻个底朝天。当然大家也可以像我一样用一些用来反编译pyc的脚本,如【uncompyle2】等等,毕竟生命在于折腾。不过配置起来比较麻烦了就是,我们目的不在于此,怎么简单怎么来,python的精神也是提倡这样的。下载地址/s/1eQy5JwQ,解压即可。至此,python这端的配置算是完成了。python的编辑器& & 虽然说脚本文件很多情况下是不用IDE(开发环境)的,记事本就可以,但是像大型的程序最好还是挑选一个智能一点的编辑器比较好。推荐SublimeText,自带python补全,小巧而强大,网上汉化版也很多,/html/24343.html。Notepad++也不错,不过我因为某些政治上的问题弃用了,现在vim用的不亦乐乎,顺便向外插usb脚踏板的emacs用户们宣战,哈哈玩笑了。python的手册& & 新手们大都要先接触python的语法啊、运算符啊、数据结构啊等看起来很高端的问题。但python作为一门相对来说简单到爆的语言,其实是非常容易上手的,计算机二级过了的筒子们基本上可以驾驭(怎么过的我就不去揣测了)。唯一不习惯的大概就是python是以缩进来代表语句块的格式了,我在此提醒一下,建议用4个空格来缩进,而不是tab键。贴上两本pdf书籍地址:《可爱的python》/s/1bndDjkj,《python核心编程》/s/1ntMEalr。Adobe Flash& & 这个大家都熟,坛子里介绍如何替换准心啥的帖子大都提到过,不多赘述。Scaleform Gfx在flash里的插件& & WG现如今的框架大概就是逐渐褪去的BigWorld引擎和Scaleform的UI系统再加上WG尚算是良心的游戏方式了。这个是我从UDK里边摘出来的。Scaleform是个商业的UI接口,售价是很高的。但UDK里有免费的授权,Epic公司实在是良心大大的好,可惜UDK被棒子的泡菜游戏搞得名声狼藉。& & 话不多说上攻略。压缩包下载地址/s/1jG0z2lC,我大概介绍一下压缩包里的内容:
17:33:01 上传
【AMP/GFxAmpClient.exe】是一个profile分析工具,大体上就是去测试一下咱开发出来的swf性能如何,占CPU多少,占内存多少等等,没多大用,但也可以显示一些调试信息,哪里出错了之类的,但仍然像我说的,没多大用。【CLIK Tools/Scaleform Extensions.mxp】这个是重点了,mxp是Adobe的插件格式,需要用【AdobeExtension Manager】这个坑爹东西去安装……
17:33:01 上传
所以用精简版的Flash的筒子们哭去吧哈哈。当然你可以单独下一个Extension Manager,但要注意几点,一是版本要对,Flash CS3的就要对应CS3的版本,CS5要对应CS5版本,二是精简版的Flash大都是不被Extension Manager识别的,所以最好用官方版本然后输入序列号,或者什么龙卷风版本啊等完整的安装版本,正版的壕请无视我,顺便求交友。& & 解压文件,准备好了之后就要打开Extension Manager,安装这个mxp格式的插件了。$ }+ G* J1 R* {
17:33:01 上传
. d" |, T$ K' e
我用的是CS3版本,这个EM也比较简陋,高版本方法是一样的。& & 安装好之后进入Flash,新建一个Flash项目,照着下边截图方式操作一次就算是配置好了。
17:33:02 上传
17:33:02 上传
! ?2 ~4 d% |: b& P0 F
17:33:03 上传
$ W0 c# |+ d/ R* h8 \! Y
; ?3 ^3 w% G5 ~' j2 N. p
17:33:03 上传
( Y6 B4 N) q7 g+ F
* e" U# e# M2 m7 y. A
& & 【Flash/AS2/CLIK】文件夹下的都是Scaleform 在Flash这一端的一些类。按照截图方式将CLIK文件夹添加到AS2的类路径中。8 Z- }! G6 B* i
17:33:04 上传
6 K0 f# M' Y2 A9 U& @) Y' F0 F
6 ]! d2 ?4 ]0 R, U! u0 s. Q8 N
到此,Flash这边就算是完事了。【硕思闪客精灵】反编译swf文件的工具& & 这个也不必多说,换准心的教程里都有介绍。我这个算是最新版本的吧,从国外扒过来的,热乎乎的英文版……地址/s/1kT2ztkF。安装好后用crack包里的exe文件覆盖原始文件,不过这个好像跟阿里巴巴反钓鱼软件有冲突……不要在意这些细节,→△→。' h. h7 C' `' B, [- P- D6 ^. G
乌拉,经过一番作死般的折腾,我们终于配置好了所有的开发环境,下一步就是开工啦。【B_i_g_W_o_r_l_d& &P_y_t_h_o_n& &D_o_c_u_m_e_n_t_a_t_i_o_n】哎呀忘了一个极其重要的东东,这玩意是我千辛万苦从一个大洋洲某小岛国上的服务器里整过来的。此时小马哥专属音乐请奏起来!!!原始地址我就不放了,怕被人找上门来……英文版,不多说,地址/s/1i3KKsfF。用浏览器打开index文件即可。这份文件我估计存活时间长不了,虽然我不明白为啥它们不做保护。
本帖最后由 狩土_默 于
02:38 编辑 6 _5 E' O$ ?% ^& A) O6 R
&documentation真取消共享啦……&
3年单野路过
新人欢迎积分1 阅读权限40积分335精华0UID帖子金钱3137 威望0
Lv.4, 积分 335, 距离下一级还需 665 积分
UID帖子威望0 多玩草50 草
探索篇:python脚本凡事都是慢慢探索出来的,做mod尤其如此。mod是如何加载的我先找到一个毛服的喊话mod,国内的“前方高能……”估计就是从那个mod改的,至于为什么选择这个,原因很简单,它有源码,有xml配置文件,这是很重要的。首先分析一下这个mod的结构,【res_mods/0.9.3/scripts/client】文件夹下便是一系列的脚本文件了,它对应了坦克世界安装目录中【res/scripts/client】文件夹,经过一番测试,我大概了解了mod的工作原理,客户端程序首先加载【res_mods】里对应版本的文件,然后再加载【res】里的,如果【res】中的同名文件已经被加载了,便跳过这个文件。如此一来便完成了客户端文件的安全替换。但是筒子们有时候发现,下载的mod里经常有一些【res】文件夹中没有的文件,如果以上推论正确的话,照理来说这些文件是不会被加载的啊?细心的筒子会发现,一般情况下,mod的【res_mods/0.9.3/scripts/client】里会有一个【mods】文件夹,还会有一个【CameraNode.pyc】文件,反编译这个文件后,呦,一切了然。我们来逐句分析这个【CameraNode.py】:+ f+ z) A8 U% X) t" u
#Embedded file name: CameraNode.py#文件名import BigWorld#导入BigWorld包, p) n9 ?4 u- k* o/ r* K&&\
class CameraNode(BigWorld.UserDataObject):#定义CameraNode类,继承自BigWorld.UserDataObject类
& &def __init__(self):#构造函数,实例化CameraNode类时会自动调用& && & BigWorld.UserDataObject.__init__(self)#调用基类的构造函数$ {( z/ h$ M&&b
def load_mods():#定义加载mod的函数,加载那些凭空出现对应不上的文件& &import ResMgr, os, glob#导入需要的包& &res = ResMgr.openSection('../paths.xml')#读取安装路径下的paths.xml文件,大家可以看一下,第一个【Path】节点便是【.res_mods/0.9.3】,倒数第二个才是【./res】,这也印证了我们上边的猜想是正确的。& &sb = res['Paths']& &vals = sb.values()[0:2]& &for vl in vals:& && &&&mp = vl.asString +'/scripts/client/mods/*.pyc'#组合成正确的加载路径& && & print '& & GF47_Console: currentpath is', mp#这是我自己debug时加的,功能是在游戏安装目录下的【python.log】文件中写一行log信息。& && & for fp in glob.iglob(mp):对于【mp】中每一条& && && &&&_, hn = os.path.split(fp)#将【fp】这个相对路径拆分成两段,一段是所在文件夹,丢弃,一段是带后缀的mod文件名,存储在【hn】中& && && &&&zn, _ = hn.split('.')#将带后缀的文件名拆分成两段,一段是不带后缀的文件名,存在zn中,一段是“pyc”这个后缀字符串,丢弃& && && &&&if zn != '__init__':& && && && && & print 'Load mods: ' + zn#在【python.log】中写一行log信息,显示当前加载的mod& && && && && & try:& && && && && && &&&exec 'import mods.' + zn#执行“importmods.zn”这段语句,zn是刚才筛选出来的mod名字。注意这句是动态执行的,脚本语言的特点……& && && && && & except Exception as err:& && && && && && &&&print 'Load mods Error:' +err#打印错误信息
8 d/ O8 ?&&@3 E2 C0 a
import sysprint '& &GF47_Console: python version is', sys.version[:5]#这也是我debug时加的,显示当前坦克世界所使用的python版本load_mods()#执行load_mods函数
我们编译一下这个【CameraNode.py】文件,方法是:打开控制台,转到当前py文件所在的文件夹,输入【compile27.py CameraNode.py】,回车后会在当前文件夹中生成CameraNode.pyc文件。呦,少年,你已经编译出了第一份pyc文件了!把这个文件拷贝到【res_mods/0.9.3/scripts/client】下。
我们可以在游戏安装目录的【python.log】文件内读取脚本文件中使用【print】语句所输出的log信息。删掉这个文件是没问题的,它会在游戏开始时再次生成。播放一次战斗回放,我们看一下它的内容:BigWorld Release Client (compiled at 15:29:52 Sep&&3 2014) starting on Sun Oct 19 21:52:49 20142 N+ p9 h" \2 |9 l) W& m6 V
& &Content Type: content6 }4 z% L/ R/ u$ @
; b/ x9 ^6 f* c( ^# b: \
Checking ./res_mods/0.9.3: mods found
& & GF47_Console: python version is 2.7.3
& & GF47_Console: current path is ./res_mods/0.9.3/scripts/client/mods/*.pyc8 Y6 W: z$ b3 Q: e: C4 h) r) b
Load mods: GunConstraints0 M' \) `* ~4 x
Load mods: MS6 Z9 k3 l5 t" `$ X5 k- s
Load mods: DamagePanel- W, R, d1 t, A3 s% y
& && &&&[ Damage Panel by GambitER 0.8.11 v.2 ]* e7 }' N4 p) G& I7 T&&x
& & GF47_Console: current path is ./res/packages/shared_content.pkg/scripts/client/mods/*.pyc/ @: n3 Q+ u& Y1 t7 _&&c4 z
[NOTE] (scripts/common/fortified_regions.py, 188): fortified_regions.init()4 b# T( R) s&&}! u6 |' J
[WARNING] (scripts/common/DestructiblesCache.py, 478): Failed to read fractureEffect name in content/Environment/env414_Pole/normal/lod0/env414_Pole.model
[WARNING] (scripts/common/DestructiblesCache.py, 478): Failed to read touchdownEffect name in content/Environment/env414_Pole/normal/lod0/env414_Pole.model7 s. Y9 f! N7 c4 L1 D; L7 ~" h# x/ c
[WARNING] (scripts/common/DestructiblesCache.py, 478): Failed to read fractureEffect name in content/Environment/env414_Pole/normal/lod0/env414_Pole10.model
[WARNING] (scripts/common/DestructiblesCache.py, 478): Failed to read touchdownEffect name in content/Environment/env414_Pole/normal/lod0/env414_Pole10.model
[NOTE] (scripts/client/post_processing/__init__.py, 267): The quality = 4 was selected.
[NOTE] (scripts/client/post_processing/__init__.py, 267): The quality = 3 was selected.: D5 V% U" W8 f) E$ c
[SPACE] Loading space: spaces/28_desert
PostProcessing.Phases.fini()复制代码5 z1 T# D$ X+ T- Q/ U" _
以GF47开头的行便是我刚才添加的debug信息了,第一个说明了当前python版本为2.7.3,第二个说明了先加载【res_mods/0.9.3/scripts/client/mods】下的所有pyc文件,Load mods开头的行显示了所加载的mod,我只安装了几个,所以只有3行,有些筒子们估计要一屏以上……
摸清了mod运行的原理,接下来就简单了,若想添加新的功能,只需把编译好的pyc文件放在【res_mods/0.9.3/scripts/client/mods】里,游戏就可以正常加载了。若想修改原始的功能,只需和【res/scripts/client】文件夹中的文件路径匹配就可以啦。
我这里只介绍原理,就不多做赘述了。贴一个喊话的mod源码,/s/1sjyPqR7,可以看一下里边的【SpotMessanger.py】文件,里边有如何加载xml配置文件的方法,还有调用聊天功能接口的函数。这个小mod还是很条理清晰的。分析已有的战斗面板接下来加快速度。我是想在战斗中显示一个独立面板的,而不是DamagePanel或debugPanel那种默认就有的面板,然后我就开始在【res】文件夹中翻找。既然是战斗中显示,那当然要优先看battle有关的脚本,果然,在【res/scripts/client/gui/Scaleform】文件夹中找到了【Battle.pyc】文件,我有一种预感,就是这个玩意了。& `: M) f# M) i9 Y
反编译后,哇哦,光是import包就有40多行代码……镇定镇定。看看它定义的类吧,Battle、TeamBasesPanel、VehicleDamageInfoPanel、DebugPanel、DamagePanel……是不是心花怒放了?乌拉!0 R1 s& N+ ]2 @& y
我在这里先提一下另外两个重要的脚本文件:flash.pyc、windows.pyc。其中flash.pyc文件定义了一个Flash类,它的功能是加载一个swf文件(泪流满面,终于找到跟swf交互的类了),而windows.pyc文件中定义了ModalWindow、BattleWindow、GUIWindow、UIInterface这几个类,除去BattleWindow这个至关重要的类外,另外三个都是与界面相关的。像是【debugPanel】,就是那个显示帧率延迟丢包的左上角的小面板,它就是继承自UIInterface的。但最开始我没有找到它是如何加载并调用swf文件的,只有Flash类里有一个直观的可以输入swf文件位置的参数,那就先用它咯。
其实选用Flash类作为基类也不是空穴来风,因为我发现安装坦克世界盒子后,这个【Battle.pyc】文件会被替换,新的文件中会多出来一个VehicleMarkersManager类,这个玩意其实是战斗统计面板,就是统计筒子们每炮输出的那个面板。这个类就是继承自Flash类的。毫无疑问,这个统计输出的面板是个新的面板,而不是客户端自带的那些,so,我就先模仿它来编写我的面板喽。+ j) M2 {8 N& X2 Z5 `, ~3 A" F" Z1 q
我们先摸清这些面板是如何被创建的,毫无疑问Battle类是最重要的,在它的构造函数__init__中,有一些初始化设置,但我最开始是肯定一头雾水的……这特么的什么玩意啊!!!耐心耐心,继续往下看。我找到了一个afterCreate函数,呦,感觉应该就是它了。一排self.__xxx = xxx(self.proxy),这当然是对xxx类的实例化,比如那个self.__debugPanel = DebugPanel(self.proxy)……再往下看,又是一堆self.__xxx.start(),看来这个start函数也是必要的了。&&z3 J9 Z) _. N6 N/ u- P
有创建当然就要有销毁,就像开机一定对应关机一样,我找到了一个beforeDelete函数,里边又是一堆self.__xxx.destroy(),这个destroy函数也是有必要的。
这样,我们就确定了一个面板的生命旅程,首先是实例化,然后是start,然后是destroy,至于中间干什么,那肯定是update啦。6 e% d. [1 y! u" z7 h
啊……如何update呢?这可是个难题,我详细看了【Battle.py】文件,发现了这个_TimeInterval类,懂英文的筒子们从名字上就知道啥意思了,时间间隔。它的功能就是每隔一段时间执行一次特定的函数,具体原理我们先不深究,大家有兴趣可以折腾一下,反正就是callback那一套了,举个栗子,我很困,于是让闹钟每隔5分钟叫醒我一次,我就可以写一段这个教程,然后又困到睡觉,再叫醒,再写一段,再睡觉……6 X) N9 c9 w' i+ s4 a3 B
好啦,python脚本这边我们算是分析完毕了,接下来就是Flash端了,毕竟UI面板是要交互的。Flash & swf可惜Flash我是不太熟悉的,只能慢慢摸索着来。那些package我先用硕思闪客精灵反编译了【debugPanel.swf】文件,这个算是最简单的swf文件了。
17:57:16 上传
解释一下图中的Actions:MainMovie是直接F9然后输入的代码,不需要选择任何元件,它的作用是全局的。sprite9中的代码是选中元件9后输入的,它作用于元件9(其实里边一个字也没有)。sprite后边有括号的,是Flash中设置过外部链接的。__packages就是外部导入的类了,其中__packages.gfx.xxxxxx格式的类我们如果正确安装了【scaleform】插件的话可以无视,不然我们费那么大劲安它干什么。而__packages.net.wargaming.ingame.xxxxxx是我们自定义的类,要按照固定路径放置,在fla工程文件同目录下建立net文件夹,在net里边建立wargaming文件夹,以此类推。' G" \! U" w* V9 R! a" i2 {& I5 s6 }
17:58:25 上传
. O% {6 Z, e# B' b- v
交互方法我最开始没有明白debugPanel.swf文件是如何与Battle.pyc中定义的DebugPanel类互相响应的。而在VehiclemarkerManager.swf中我找到了这个addCallBack函数,它向客户端注册了一个swf里的函数,然后python脚本便可以根据注册时传入的函数名字“call”这个函数了。哇哦,总算搞明白了python中Flash类里的call函数(其实是invoke函数调用的swf里的函数,call函数只是对invoke进行了封装)。相应的respond也是一样,区别就是,call是python调用swf里的函数,而respond是swf文件调用python脚本里的函数。后来我看了Scaleform Gfx的《Getting Started》/s/1mgyt5qw,里边标准的交互方法也确实是这样的。还有一本《Technical Appendix》/s/1dDjEH01,这两个doc便详细介绍了交互方法,英文,真是残念……( i$ y3 A/ E" y$ K) [- v5 w
; p" L* |, l* w& H# L
% N&&T9 K. _/ M; e1 M3 i
$ J' f$ |7 m. ~/ N1 ~
以上,我们便掌握了python脚本、flash制作swf文件、两者之间交互方式。我又一不小心找到了BigWorld的A_P_I,详细一切都准备好啦!!!
本帖最后由 狩土_默 于
18:52 编辑 + O9 I% w" C+ c. x/ f
3年单野路过
新人欢迎积分1 阅读权限40积分335精华0UID帖子金钱3137 威望0
Lv.4, 积分 335, 距离下一级还需 665 积分
UID帖子威望0 多玩草50 草
实战篇终于到了上手的阶段,经过了前几篇的折腾,头已经开始嗡嗡嗡了……* f$ q: K0 P' R% L5 @/ U
2 m4 C: q+ c3 _* M
新面板的python类还是先从python这边入手。在反编译后的【Battle.py】文件最后添加我们自己的内容。class GF47TextPanel(Flash):0 w' |6 j* o% [5 ~' t( W, ?
& & __SWF_FIEL_NAME__ = &GF47TextPanel.swf&
3 O5 s5 M# K: p. V6 I1 R0 W. V& z
& & def __init__(self):
& && &&&Flash.__init__(self, self.__SWF_FIEL_NAME__)
& && &&&self.__msg = &&7 b9 u8 _3 l) ~
& && &&&self.__timeInterval = None复制代码
首先建立一个名为GF47TextPanel(名字你们就无视好了)的继承自Flash基类的类,然后定义一个存储了所要加载的swf名字的字符串__SWF_FIEL__NAME__,再后定义构造函数(__init__,这是每个类都必须的东西,而__del__析构函数不是必须的)。然后继续输入以下代码:
2 f/ u4 O/ o3 L! D% S$ E
& & def start(self):& && & self.active(True)#将自身是否显示设为“显示”& && & self.__setCandidate(&测试是否可以显示文字&)#下文会提到,更改面板上的信息& && & GUI.ponent)#必须要有的,向GUI root注册自身的UIcomponent,当然也可以像其他的UI面板一样将自身注册到Battle类上,但我们先把这个框架搭起来,以后再优化。& && & ponent.wg_inputKeyMode = 2#A_P_I里没有,wg自己添加的东西,大概是设置键盘事件的接收优先模式吧。反正不设置这个的话,默认是1,然后你的键盘就不管用了……& && & ponent.drawWithRestrictedViewPort = False#暂时不知道啥作用& && & self.movie.backgroundAlpha = 0#看名知意,设置背景透明度,你可以设置为1……& && & self.__timeInterval = _TimeInterval(0.1, '_GF47TextPanel__update',weakref.proxy(self))#实例化一个“时间间隔”,作用是每隔0.1秒激活一次“_GF47TextPanel”里的“__update”函数。后边那个weakref.proxy(self)是建立一个自身的弱引用,大概意思就是比如一个上战场的老兵临行前把老婆托付给了自己隔壁的二狗,假如自己挂了,老婆也不至于晒床单没有帮手,老兵就是这个self,隔壁的二狗就是weakref.proxy(self)……突然感觉好悲伤……& && & self.__timeInterval.start()#启动“时间间隔”,开始每0.1秒执行一次“__update”& && & self.__update()#可以加上,也可以去掉
我上文分析过,这个类是需要一个启动的阀门的——start,我们不可能在构造函数里做这些工作,因为那时候Battle类的初始化还没完成呢。
然后是destroy():- [6 r&&R+ ^. J, D+ P$ i, e9 h
& & def destroy(self):
& && &&&self.__timeInterval.stop()
& && &&&GUI.ponent)0 ^1 v# V/ \9 n1 g% b6 h
& && &&&self.close()
6 k0 H/ c0 `* u6 s* c( x5 |% `
当战斗结束后,销毁战斗面板时触发的事件。再后是__update():& & def __update(self):3 }1 e3 V/ C5 g- |# D
& && &&&pass#先跳过,这里的东西战斗中每隔一段时间便执行一次,以后筒子们添加东西大都要在这里做文章。复制代码
最后是与swf文件交互的函数:& & def __setCandidate(self, msg):4 B7 z9 t. H- C$ o* R5 m, i# V2 t&&x3 o
& && &&&self.call(&GF47TextPanel.setAndShowCandidate&, [msg])#记得这个“call”不,它呼叫swf文件中注册的那个“GF47TextPanel.setAndShowCandidate”函数,并将参数“msg”封装成一个列表一并传递过去。“msg”在本例中就是一串字符串了。复制代码8 \+ H4 {% j&&l! b# f4 d) `7 ^8 C0 J
OK,这个“GF47TextPanel”类我们就完成了。& u. y& l, i& h( b( j% v
( V: _1 Q) ^4 {: s9 N7 t, C5 E
将“GF47TextPanel”添加到战斗中但貌似我们创建的类目前为止没有和Battle类产生任何关联,我们接下来将模仿那些debugPanel、DamagePanel等,将start与destroy添加到Battle类中。在【Battle.py】文件中找到之前分析的Battle类,在afterCreate函数中照葫芦画瓢,在那一列“self.__xxx = xxx(self.proxy)”后添加:self.__GF47TextPanel = GF47TextPanel()复制代码在那列“self.__xxx.start()”后添加:self.__GF47TextPanel.start()复制代码在Battle类的beforeDelete函数中,找到那一列“self.__xxx.destroy()”,在后边添加:self.__GF47TextPanel.destroy()复制代码如此这般,我们的新面板就可以在战斗中显示啦。但光这样其实是不够的,假如现在就完事的话,我们会发现,当战斗中使用快捷键“v”,就是那个隐藏所有面板的键时,我们的新面板仍然固执地停留在屏幕上,于是我们可以找到Battle类中的showAll函数,在它最后添加一句:self.__GF47TextPanel.active(isShow)复制代码这样一来,快捷键“v”也对我们的新面板起作用了。0 A5 h4 W: @; h0 ~- @
好了,编译这个【Battle.py】文件,如果有错误就把“#”号和注释删掉。在【res_mods/0.9.3/scripts/client】文件夹(就是CameraNode.pyc所在的文件夹)中新建【gui/Scaleform】,还记得为什么不?要保持路径相同。将编译好的【Battle.pyc】文件拷贝到里边。
9 ^* E% H8 ]+ g. C1 ~4 L
制作swf文件接下来开始制作与GF47TextPanel类相交互的swf文件。
工程设置新建一个AS2工程,大小为750x32像素,你们随便。帧率选30,好像那些原有的面板就是30的帧率。没必要发布HTML版本的,所以去掉勾勾。Flash版本其实没多大关系,我选的flash8。&&t2 E' R% N3 _& Y
18:09:01 上传
0 [! s( C. J$ N& O6 x" o7 R" @
添加动态文本和背景在合适的位置上添加一个动态文本。给动态文本起一个合适的实例名称,这样它就可以在ActionScript里被直接访问了,我的是GF47Text。为了安全起见,我给swf嵌入了中文字库,和必要的拉丁字母。0 w" ~0 @) a&&P( w4 F
18:10:07 上传
1 V' I&&c1 [. @* @
我新建了一个层,作为背景,就是简单地画了个框框,然后转换成了元件,也起了个实例名。
添加AS2脚本按下“F9”,打开脚本编辑界面,输入以下代码:import gfx.io.GameD//导入GameDelegate包
Stage.scaleMode = &noScale&;//设置缩放方式为不缩放
Stage.align = &BL&;//设置对齐方式为左下角对其
GF47Text.text = &Made by GF47&;//设置动态文本的text属性值为“Made by GF47”
' w, `' P3 B' p1 e
GameDelegate.addCallBack(&GF47TextPanel.setAndShowCandidate&, this, &setAndShowCandidate&);//将“setAndShowCandidate”函数注册到游戏中,函数名为“GF47TextPanel.setAndShowCandidate”,与python脚本中的“call”相交互
GameDelegate.addCallBack(&GF47TextPanel.showCandidate&, this, &showCandidate&);//与上句差不多意思+ ]8 M&&w4 ], C& c6 _
) ^) Z* Y5 G# U8 u/ f1 h% ^
function setAndShowCandidate(msg:String):Void//定义“setAndShowCandidate”函数
{2 w$ O% l8 ]3 t* j&&[8 d
& & GF47TextBackground._visible =( `7 [3 O# X, \. A: R! o
& & GF47Text._visible =' z0 }& b6 J) W
& & GF47Text.text =//改变动态文本的text属性值,显示“msg”内容5 Z! E, {8 J* r$ e' O+ V
}! M) y( Z$ D- g6 t&&P8 J8 J
&&H8 }- M0 T" J+ v) R' X$ i
function showCandidate(isShow:Boolean):Void//显示或隐藏7 `6 u9 ^2 A4 T2 V1 f
{' ^1 ~+ `9 l( D6 @/ s) R
& & GF47TextBackground._visible = isS
& & GF47Text._visible = isS8 B' P8 Y* r2 O+ e) h/ l
}复制代码: z7 s9 b# u" E, p, S+ c
GameDelegate.addCallBack这个函数便是交互的关键了,上文已经说过了,不明白可以查看那两个文档。总之以后我们就用这种方式来交互。本例中没有设置respond。
发布swf文档完成上边的操作后,我们就可以发布啦。点击那个scaleform插件的发布按钮,我们的swf就发布出来了。发布的swf名称要与python脚本中设置的那个“__SWF_FILE_NAME__”相吻合。/ V- Q1 `# Z0 b6 D5 E* w
18:12:46 上传
18:24:37 上传
将发布出来的swf拷贝到【res_mods/0.9.3/gui/scaleform】中,大功告成。( `* r0 q/ z" s
测试打开一个战斗回放,呦,已经有了啊,并且它的文本已经改变了。在脚本中的“__update”函数中我们并没有设置什么,我们只是在“start”中改变了一下文本的值。如果想在战斗中更新文本,比如显示200米范围内敌军数量,友军数量等(这只是一个例子,我可能会以后把它实现,毕竟ime的问题失败了),可以在update函数中添相应代码,最后将处理好的文本传给swf文件。
本帖最后由 狩土_默 于
02:37 编辑
(322.65 KB, 下载次数: 1)
18:26:02 上传
下载次数: 1
&牛!字太多!慢慢看!&
3年单野路过
新人欢迎积分1 阅读权限40积分335精华0UID帖子金钱3137 威望0
Lv.4, 积分 335, 距离下一级还需 665 积分
UID帖子威望0 多玩草50 草
我本来是想解决战斗中输入法候选框不显示的问题的,找到的BW_A_P_I里也有PyIME类,脚本中也可以引用BigWorld.ime. candidatesVisible,但偏偏它一直是False状态。后来我看Scaleform文档,发现新的Scaleform好像已经有了对东亚IME的支持,也就是说,WG大概是用了Scaleform的方法,而屏蔽了BigWorld的默认方式……/ \$ F* K2 Y4 ~6 C4 u&&s# L
0 k* d) [, t6 q" L% `( @
(╯°Д°)╯︵ ┻━┻ 特么的你玩我啊!!!
之后我看到【res/gui/flash】文件夹里有【ime.swf】文件,反编译了一下,果然,虽然这是个AS3版本的文件,但还是很明了。Scaleform的方式是在swf文件中处理东亚字符的所有问题,传给游戏的只是最终选择的那个词组,也就是说,所有问题都处在这个swf文件上。这个问题找个会AS3的人,比如做网页的,解决起来是很简单的……但是我是不太懂Flash……不知道二雷连这么个错误都能拖n个版本为啥……好吧我这几天去补一下AS3……说点题外话,其实说起来flash技术已经有些过时了,这年头google苹果大微软基本都要把flash这玩意撇开自己单干,简单来说就是不带你玩了,这就是没有像格力那样掌握核心科技的后果,被人掐着脖子走。所以华为那种方式才是正道啊,有实力,敢说话,不用看别人脸色,哪像二雷这种坑货,连个swf都不敢改……哎不说了,有打广告的嫌疑……
6 t3 I: U9 ]9 m1 u" V7 T3 d
; Y& s8 A9 o7 P4 c' s, {2 y
好啦,这篇教程就到这里了。我仅仅是搭了一个mod的基本框架,筒子们可以用它来实现各种功能。希望大家发挥想象,折腾的愉快……, v3 A* i4 S5 ~: l
最后附上一张截图,我大多炮塔神教万岁!!!- ~+ a! f! j% ]8 u: X$ d
18:46:19 上传
本帖最后由 狩土_默 于
18:57 编辑
3年单野路过
新人欢迎积分1 阅读权限60积分3228精华0UID帖子金钱25587 威望-2
Lv.6, 积分 3228, 距离下一级还需 1772 积分
UID帖子威望-2 多玩草446 草
牛!字太多!慢慢看!
新人欢迎积分0 阅读权限70积分9014精华0UID帖子金钱56523 威望0
Lv.7, 积分 9014, 距离下一级还需 986 积分
UID帖子威望0 多玩草483 草
这技术帖很不错啊,多谢楼主!!
新人欢迎积分0 阅读权限50积分1546精华0UID帖子金钱778 威望0
Lv.5, 积分 1546, 距离下一级还需 954 积分
UID帖子威望0 多玩草0 草
能不能给来个现成的
秋山S优花里
新人欢迎积分0 阅读权限50积分2253精华0UID帖子金钱19300 威望0
Lv.5, 积分 2253, 距离下一级还需 247 积分
UID帖子威望0 多玩草0 草
谢谢楼主的教程~~~' \" C: ]* f: C; @7 R9 B% r
吾辈是学渣,所以纯支持了~~~
傲嬌重巡加藤惠
新人欢迎积分0 阅读权限60积分3406精华0UID帖子金钱25499 威望0
Lv.6, 积分 3406, 距离下一级还需 1594 积分
UID帖子威望0 多玩草638 草
一个只会点涂装和稍微改点模型的支持一下撸主这种技术宅
专注各种风格的涂装30年
新人欢迎积分0 阅读权限40积分973精华0UID帖子金钱3322 威望0
Lv.4, 积分 973, 距离下一级还需 27 积分
UID帖子威望0 多玩草35 草
天降-小裤裤
新人欢迎积分0 阅读权限80积分13803精华0UID帖子金钱359275 威望4
【小心被屏蔽】
Lv.8, 积分 13803, 距离下一级还需 6197 积分
UID帖子威望4 多玩草1451 草
新人欢迎积分1 阅读权限90积分30149精华0UID145100帖子金钱416901 威望1
注册时间:号17点05分
Lv.9, 积分 30149, 距离下一级还需 4851 积分
UID145100帖子威望1 多玩草2330 草
新人欢迎积分1 阅读权限50积分1288精华0UID帖子金钱27327 威望-2
Lv.5, 积分 1288, 距离下一级还需 1212 积分
UID帖子威望-2 多玩草115 草
新人欢迎积分1 阅读权限80积分10551精华0UID帖子金钱52283 威望4
Lv.8, 积分 10551, 距离下一级还需 9449 积分
UID帖子威望4 多玩草2215 草
技术贴赞一个!
SerB wrote:
Do not play on the German tanks.
Do not play on Soviet tanks.
Do not play on American tanks.
Do not play on Chinese tanks.
Do not play on the French tanks.
Do not play on British tanks!
Do not play team games.
[img]http://att./forum//101359z
马年新春勋章
手机APP马年迎春,马上有钱!
元宝专属一阶勋章。已绝版
365天!天天有你
连续签到1年即可获得
YY论坛外管
YY论坛外管
手机论坛勋章
APP发帖双倍积分,登陆即送勋章!
需要金钱:1100
手机盒子客户端点击或扫描下载
Powered by

我要回帖

更多关于 python 创建用户 的文章

 

随机推荐