Python算术运算符
运算符 | 描述 | 实例 |
---|---|---|
+ | 加 - 两个对象相加 | a + b输出结果 30 |
- | 减 - 得到负数或是一个数减去另一个数 | a - b 输出结果 -10 |
* | 乘 - 两个数相乘或是返回一个被重复若干次的字符串 | a * b 输出结果 200 |
/ | 除 - x除以y | b / a 输出结果 2 |
% | 取模 - 返回除法的余数 | b % a 输出结果 0 |
** | 幂 - 返回x的y次幂 | a**b 为10的20次方, 输出结果 100000000000000000000 |
// | 取整除 - 返回商的整数部分 | 9//2 输出结果 4 , 9.0//2.0 输出结果 4.0 |
Python逻辑运算符
运算符 | 描述 | 实例 |
---|---|---|
and | 布尔”与” - 如果x为False,x and y返回False,否则它返回y的计算值。 | (a and b) 返回 true。 |
or | 布尔”或” - 如果x是True,它返回True,否则它返回y的计算值. | (a or b) 返回 true。 |
not | 布尔”非” - 如果x为True,返回False。如果x为False,它返回True。 | not(a and b) 返回 false。 |
Python成员运算符
运算符 | 描述 | 实例 |
---|---|---|
in | 如果在指定的序列中找到值返回True,否则返回False. | x 在 y序列中 , 如果x在y序列中返回True。 |
not in | 如果在指定的序列中没有找到值返回True,否则返回False。 | x 不在 y序列中 , 如果x不在y序列中返回True。 |
#!/usr/bin/python
a = 10
b = 20
list = [1, 2, 3, 4, 5 ];
if ( a in list ):
print "Line 1 - a is available in the given list"
else:
print "Line 1 - a is not available in the given list"
if ( b not in list ):
print "Line 2 - b is not available in the given list"
else:
print "Line 2 - b is available in the given list"
Python身份运算符
身份运算符用于比较两个对象的存储单元
运算符 | 描述 | 实例 |
---|---|---|
is | is是判断两个标识符是不是引用自一个对象 | x is y, 如果 id(x) 等于 id(y) , is 返回结果 1 |
is not | is not是判断两个标识符是不是引用自不同对象 | x is not y, 如果 id(x) 不等于 id(y). is not 返回结果 1 |
if (a is b):
print "Line 1 - a and b have same identity"
else:
print "Line 1 - a and b do not have same identity"
print "id(a)", id(a)
print "id(b)", id(b)
if (id(a) == id(b)):
print "Line 2 - a and b have same identity"
else:
print "Line 2 - a and b do not have same identity"
if (id(a) is id(b)):
print "Line 2 - a and b have same identity"
else:
print "Line 2 - a and b do not have same identity"
Python 中文编码
当碰到编码问题时候只要在文件开头加入`# -*- coding: UTF-8 -*-` 或者 `#coding=utf-8`就行了
#coding=utf-8
#!/usr/bin/python
int "你好,世界";
Python标识符
在python里,标识符有字母、数字、下划线组成。
在python中,所有标识符可以包括英文、数字以及下划线(_),但不能以数字开头。
python中的标识符是区分大小写的。
以单下划线开头(_foo)的代表不能直接访问的类属性,需通过类提供的接口进行访问,不能用"from xxx import *"而导入;
以双下划线开头的(__foo)代表类的私有成员;
以双下划线开头和结尾的(__foo__)代表python里特殊方法专用的标识,如__init__()代表类的构造函数。
Python保留字符
这些保留字不能用作常数或变数,或任何其他标识符名称
and | exec | not |
---|---|---|
assert | finally | or |
break | for | pass |
class | from | |
continue | global | raise |
def | if | return |
del | import | try |
elif | in | while |
else | is | with |
except | lambda | yield |
行和缩进
Python的代码块不使用大括号({})来控制类,函数以及其他逻辑判断。python最具特色的就是用缩进来写模块
缩进的空白数量是可变的,但是所有代码块语句必须包含相同的缩进空白数量,这个必须严格执行
多行语句
可以使用斜杠( \)将一行的语句分为多行显示
total = item_one + \
item_two + \
item_three
语句中包含[], {} 或 () 括号就不需要使用多行连接符
days = ['Monday', 'Tuesday', 'Wednesday',
'Thursday', 'Friday']
Python引号
Python 接收单引号(' ),双引号(" ),三引号(''' """) 来表示字符串,引号的开始与结束必须的相同类型的。
三引号可以由多行组成,编写多行文本的快捷语法
Python注释
python中单行注释采用 # 开头。
python没有块注释,所以现在推荐的多行注释也是采用的#
等待用户输入
raw_input("\n\nPress the enter key to exit.")
同一行显示多条语句
Python可以在同一行中使用多条语句,语句之间使用分号(;)分割
Python 变量类型
Python中的变量不需要声明,变量的赋值操作既是变量声明和定义的过程。 每个变量在使用前都必须赋值,变量赋值以后该变量才会被创建。
多个变量赋值
a = b = c = 1
a, b, c = 1, 2, "john"
标准数据类型
Numbers(数字) String(字符串) List(列表) Tuple(元组) Dictionary(字典)
Python数字
int(有符号整型) long(长整型[也可以代表八进制和十六进制]) float(浮点型) complex(复数) > 长整型也可以使用小写"L",但是还是建议您使用大写"L",避免与数字"1"混淆。Python使用"L"来显示长整型。
Python字符串
字符串或串(String)是由数字、字母、下划线组成的一串字符。
python的字串列表有2种取值顺序:
从左到右索引默认0开始的,最大范围是字符串长度少1 从右到左索引默认-1开始的,最大范围是字符串开头
如果你的实要取得一段子串的话,可以用到变量[头下标:尾下标],就可以截取相应的字符串,其中下标是从0开始算起,可以是正数或负数,下标可以为空表示取到头或尾。 加号(+)是字符串连接运算符,星号(*)是重复操作
#coding=utf-8
#!/usr/bin/python
str = 'Hello World!'
print str # 输出完整字符串
print str[0] # 输出字符串中的第一个字符
print str[2:5] # 输出字符串中第三个至第五个之间的字符串
print str[2:] # 输出从第三个字符开始的字符串
print str[:-2]
print str * 2 # 输出字符串两次
print str + "TEST" # 输出连接的字符串
Python列表
列表可以完成大多数集合类的数据结构实现。它支持字符,数字,字符串甚至可以包含列表(所谓嵌套) 列表用[ ]标识。是python最通用的复合数据类型。 > 列表中的值得分割也可以用到变量[头下标:尾下标],就可以截取相应的列表,从左到右索引默认0开始的,从右到左索引默认-1开始,下标可以为空表示取到头或尾。 > 加号(+)是列表连接运算符,星号(*)是重复操作。
#coding=utf-8
#!/usr/bin/python
list = [ 'abcd', 786 , 2.23, 'john', 70.2 ]
tinylist = [123, 'john']
print list # 输出完整列表
print list[0] # 输出列表的第一个元素
print list[1:3] # 输出第二个至第三个的元素
print list[2:] # 输出从第三个开始至列表末尾的所有元素
print tinylist * 2 # 输出列表两次
print list + tinylist # 打印组合的列表
Python元组
元组是另一个数据类型,类似于List(列表) 元组用"()"标识。内部元素用逗号隔开。但是元素不能二次赋值,相当于只读列表。
#coding=utf-8
#!/usr/bin/python
tuple = ( 'abcd', 786 , 2.23, 'john', 70.2 )
tinytuple = (123, 'john')
print tuple # 输出完整元组
print tuple[0] # 输出元组的第一个元素
print tuple[1:3] # 输出第二个至第三个的元素
print tuple[2:] # 输出从第三个开始至列表末尾的所有元素
print tinytuple * 2 # 输出元组两次
print tuple + tinytuple # 打印组合的元组
以下是元组无效的,因为元组是不允许更新的。而列表是允许更新的:
#coding=utf-8
#!/usr/bin/python
tuple = ( 'abcd', 786 , 2.23, 'john', 70.2 )
list = [ 'abcd', 786 , 2.23, 'john', 70.2 ]
tuple[2] = 1000 # 元组中是非法应用
list[2] = 1000 # 列表中是合法应用
Python元字典
列表是有序的对象结合,字典是无序的对象集合 两者之间的区别在于:字典当中的元素是通过键来存取的,而不是通过偏移存取。 字典用"{ }"标识。字典由索引(key)和它对应的值value组成。
#coding=utf-8
#!/usr/bin/python
dict = {}
dict['one'] = "This is one"
dict[2] = "This is two"
tinydict = {'name': 'john','code':6734, 'dept': 'sales'}
print dict['one'] # 输出键为'one' 的值
print dict[2] # 输出键为 2 的值
print tinydict # 输出完整的字典
print tinydict.keys() # 输出所有键
print tinydict.values() # 输出所有值
Python数据类型转换
函数 | 描述 |
---|---|
int(x [,base]) | 将x转换为一个整数 |
long(x [,base] ) | 将x转换为一个长整数 |
float(x) | 将x转换到一个浮点数 |
complex(real [,imag]) | 创建一个复数 |
str(x) | 将对象 x 转换为字符串 |
repr(x) | 将对象 x 转换为表达式字符串 |
eval(str) | 用来计算在字符串中的有效Python表达式,并返回一个对象 |
tuple(s) | 将序列 s 转换为一个元组 |
list(s) | 将序列 s 转换为一个列表 |
set(s) | 转换为可变集合 |
dict(d) | 创建一个字典。d 必须是一个序列 (key,value)元组。 |
frozenset(s) | 转换为不可变集合 |
chr(x) | 将一个整数转换为一个字符 |
unichr(x) | 将一个整数转换为Unicode字符 |
ord(x) | 将一个字符转换为它的整数值 |
hex(x) | 将一个整数转换为一个十六进制字符串 |
oct(x) | 将一个整数转换为一个八进制字符串 |
Python提供了for循环和while循环(在Python中没有do..while循环)
循环使用 else 语句
在 python 中,for … else 表示这样的意思,for 中的语句和普通的没有区别,else 中的语句会在循环正常执行完(即 for 不是通过 break 跳出而中断的)的情况下执行,while … else 也是一样
#!/usr/bin/python
count = 0
while count < 5:
print count, " is less than 5"
count = count + 1
else:
print count, " is not less than 5"
Python for 循环语句
Python for循环可以遍历任何序列的项目,如一个列表或者一个字符串。
for循环的语法格式如下:
for iterating_var in sequence:
statements(s)
#!/usr/bin/python
for letter in 'Python': # First Example
print 'Current Letter :', letter
fruits = ['banana', 'apple', 'mango']
for fruit in fruits: # Second Example
print 'Current fruit :', fruit
print "Good bye!"
通过序列索引迭代
#!/usr/bin/python
fruits = ['banana', 'apple', 'mango']
for index in range(len(fruits)):
print 'Current fruit :', fruits[index]
print "Good bye!"
以上实例我们使用了内置函数 len() 和 range(),函数 len() 返回列表的长度,即元素的个数。 range返回一个序列的数。s
循环使用 else 语句
#!/usr/bin/python
for num in range(10,20): #to iterate between 10 to 20
for i in range(2,num): #to iterate on the factors of the number
if num%i == 0: #to determine the first factor
j=num/i #to calculate the second factor
print '%d equals %d * %d' % (num,i,j)
break #to move to the next number, the #first FOR
else: # else part of the loop
print num, 'is a prime number'
Python pass 语句
python pass是空语句,是为了保持程序结构的完整性。
Python 数字数据类型用于存储数值。
数据类型是不允许改变的,这就意味着如果改变数字数据类型得值,将重新分配内存空间。
您也可以使用del语句删除一些数字对象引用。
del语句的语法是:
del var1[,var2[,var3[....,varN]]]]
Python 支持四种不同的数值类型
整型(Int) - 通常被称为是整型或整数,是正或负整数,不带小数点
长整型(long integers) - 无限大小的整数,整数最后是一个大写或小写的L。
浮点型(floating point real values) - 浮点型由整数部分与小数部分组成,浮点型也可以使用科学计数法表示(2.5e2 = 2.5 x 102 = 250)
复数( (complex numbers)) - 复数的虚部以字母J 或 j结尾 。如:2+3i
Python数字类型转换
int(x [,base ]) 将x转换为一个整数
long(x [,base ]) 将x转换为一个长整数
float(x ) 将x转换到一个浮点数
complex(real [,imag ]) 创建一个复数
str(x ) 将对象 x 转换为字符串
repr(x ) 将对象 x 转换为表达式字符串
eval(str ) 用来计算在字符串中的有效Python表达式,并返回一个对象
tuple(s ) 将序列 s 转换为一个元组
list(s ) 将序列 s 转换为一个列表
chr(x ) 将一个整数转换为一个字符
unichr(x ) 将一个整数转换为Unicode字符
ord(x ) 将一个字符转换为它的整数值
hex(x ) 将一个整数转换为一个十六进制字符串
oct(x ) 将一个整数转换为一个八进制字符串
Python数学函数
函数 | 返回值 ( 描述 ) |
---|---|
abs(x) | 返回数字的绝对值,如abs(-10) 返回 10 |
ceil(x) | 返回数字的上入整数,如math.ceil(4.1) 返回 5 |
cmp(x, y) | 如果 x < y 返回 -1, 如果 x == y 返回 0, 如果 x > y 返回 1 |
exp(x) | 返回e的x次幂(ex),如math.exp(1) 返回2.718281828459045 |
fabs(x) | 返回数字的绝对值,如math.fabs(-10) 返回10.0 |
floor(x) | 返回数字的下舍整数,如math.floor(4.9)返回 4 |
log(x) | 如math.log(math.e)返回1.0,math.log(100,10)返回2.0 |
log10(x) | 返回以10为基数的x的对数,如math.log10(100)返回 2.0 |
max(x1, x2,…) | 返回给定参数的最大值,参数可以为序列。 |
min(x1, x2,…) | 返回给定参数的最小值,参数可以为序列。 |
modf(x) | 返回x的整数部分与小数部分,两部分的数值符号与x相同,整数部分以浮点型表示。 |
pow(x, y) | x**y 运算后的值。 |
round(x [,n]) | 返回浮点数x的四舍五入值,如给出n值,则代表舍入到小数点后的位数。 |
sqrt(x) | 返回数字x的平方根,数字可以为负数,返回类型为实数,如math.sqrt(4)返回 2+0j |
Python随机数函数
函数 | 描述 |
---|---|
choice(seq) | 从序列的元素中随机挑选一个元素,比如random.choice(range(10)),从0到9中随机挑选一个整数。 |
randrange ([start,] stop [,step]) | 从指定范围内,按指定基数递增的集合中获取一个随机数,基数缺省值为1 |
random() | 随机生成下一个实数,它在[0,1)范围内。 |
seed([x]) | 改变随机数生成器的种子seed。如果你不了解其原理,你不必特别去设定seed,Python会帮你选择seed。 |
shuffle(lst) | 将序列的所有元素随机排序 |
uniform(x, y) | 随机生成下一个实数,它在[x,y]范围内。 |
Python三角函数
函数 | 描述 |
---|---|
acos(x) | 返回x的反余弦弧度值。 |
asin(x) | 返回x的反正弦弧度值。 |
atan(x) | 返回x的反正切弧度值。 |
atan2(y, x) | 返回给定的 X 及 Y 坐标值的反正切值。 |
cos(x) | 返回x的弧度的余弦值。 |
hypot(x, y) | 返回欧几里德范数 sqrt(xx + yy)。 |
sin(x) | 返回的x弧度的正弦值。 |
tan(x) | 返回x弧度的正切值。 |
degrees(x) | 将弧度转换为角度,如degrees(math.pi/2) , 返回90.0 |
radians(x) | 将角度转换为弧度 |
Python数学常量
常量 | 描述 |
---|---|
pi | 数学常量 pi(圆周率,一般以π来表示) |
e | 数学常量 e,e即自然常数(自然常数)。 |
Python访问字符串中的值
Python不支持单字符类型,单字符也在Python也是作为一个字符串使用。
Python不支持单字符类型,单字符也在Python也是作为一个字符串使用。
#!/usr/bin/python
var1 = 'Hello World!'
var2 = "Python Programming"
print "var1[0]: ", var1[0]
print "var2[1:5]: ", var2[1:5]
Python字符串运算符
下表实例变量a值为字符串”Hello”,b变量值为”Python”:
操作符 | 描述 | 实例 |
---|---|---|
+ | 字符串连接 | a + b 输出结果: HelloPython |
* | 重复输出字符串 | a*2 输出结果:HelloHello |
[] | 通过索引获取字符串中字符 | a[1] 输出结果 e |
[ : ] | 截取字符串中的一部分 | a[1:4] 输出结果 ell |
in | 成员运算符 - 如果字符串中包含给定的字符返回 True | H in a 输出结果 1 |
not in | 成员运算符 - 如果字符串中不包含给定的字符返回 True | M not in a 输出结果 1 |
r/R | 原始字符串 -原始字符串:所有的字符串都是直接按照字面的意思来使用,没有转义特殊或不能打印的字符。 原始字符串除在字符串的第一个引号前加上字母”r”(可以大小写)以外,与普通字符串有着几乎完全相同的语法。 | print r’\n’ prints \n 和 print R’\n’ prints \n |
% | 格式字符串 | 请看下一章节 |
Python字符串格式化
#!/usr/bin/python
print "My name is %s and weight is %d kg!" % ('Zara', 21)
python字符串格式化符号:
符 号 | 描述 |
---|---|
%c | 格式化字符及其ASCII码 |
%s | 格式化字符串 |
%d | 格式化整数 |
%u | 格式化无符号整型 |
%o | 格式化无符号八进制数 |
%x | 格式化无符号十六进制数 |
%X | 格式化无符号十六进制数(大写) |
%f | 格式化浮点数字,可指定小数点后的精度 |
%e | 用科学计数法格式化浮点数 |
%E | 作用同%e,用科学计数法格式化浮点数 |
%g | %f和%e的简写 |
%G | %f 和 %E 的简写 |
%p | 用十六进制数格式化变量的地址 |
Python三引号(triple quotes)
python中三引号可以将复杂的字符串进行复制
python三引号允许一个字符串跨多行,字符串中可以包含换行符、制表符以及其他特殊字符
三引号的语法是一对连续的单引号或者双引号(通常都是成对的用)
Unicode 字符串
u'Hello World !'
引号前小写的"u"表示这里创建的是一个 Unicode 字符串
python的字符串内建函数
方法 | 描述 |
---|---|
string.capitalize() | 把字符串的第一个字符大写 |
string.center(width) | 返回一个原字符串居中,并使用空格填充至长度 width 的新字符串 |
string.count(str, beg=0, end=len(string)) | 返回 str 在 string 里面出现的次数,如果 beg 或者 end 指定则返回指定范围内 str 出现的次数 |
string.decode(encoding=’UTF-8’, errors=’strict’) | 以 encoding 指定的编码格式解码 string,如果出错默认报一个 ValueError 的 异 常 , 除 非 errors 指 定 的 是 ‘ignore’ 或 者’replace’ |
string.encode(encoding=’UTF-8’, errors=’strict’) | 以 encoding 指定的编码格式编码 string,如果出错默认报一个ValueError 的异常,除非 errors 指定的是’ignore’或者’replace’ |
string.endswith(obj, beg=0, end=len(string)) | 检查字符串是否以 obj 结束,如果beg 或者 end 指定则检查指定的范围内是否以 obj 结束,如果是,返回 True,否则返回 False. |
string.expandtabs(tabsize=8) | 把字符串 string 中的 tab 符号转为空格,默认的空格数 tabsize 是 8. |
string.find(str, beg=0, end=len(string)) | 检测 str 是否包含在 string 中,如果 beg 和 end 指定范围,则检查是否包含在指定范围内,如果是返回开始的索引值,否则返回-1 string.index(str, beg=0, end=len(string))跟find()方法一样,只不过如果str不在 string中会报一个异常. |
string.isalnum() | 如果 string 至少有一个字符并且所有字符都是字母或数字则返回 True,否则返回 False |
string.isalpha() | 如果 string 至少有一个字符并且所有字符都是字母则返回 True,否则返回 False |
string.isdecimal() | 如果 string 只包含十进制数字则返回 True 否则返回 False. 这种方法只存在于unicode对象。 |
string.isdigit() | 如果 string 只包含数字则返回 True 否则返回 False. |
string.islower() | 如果 string 中包含至少一个区分大小写的字符,并且所有这些(区分大小写的)字符都是小写,则返回 True,否则返回 False |
string.isnumeric() | 如果 string 中只包含数字字符,则返回 True,否则返回 False . 这种方法是只针对unicode对象 |
string.isspace() | 如果 string 中只包含空格,则返回 True,否则返回 False. |
string.istitle() | 如果 string 是标题化的(见 title())则返回 True,否则返回 False |
string.isupper() | 如果 string 中包含至少一个区分大小写的字符,并且所有这些(区分大小写的)字符都是大写,则返回 True,否则返回 False |
string.join(seq) | Merges (concatenates)以 string 作为分隔符,将 seq 中所有的元素(的字符串表示)合并为一个新的字符串 |
string.ljust(width) | 返回一个原字符串左对齐,并使用空格填充至长度 width 的新字符串 |
string.lower() | 转换 string 中所有大写字符为小写. |
string.lstrip() | 截掉 string 左边的空格 |
string.maketrans(intab, outtab]) | maketrans() 方法用于创建字符映射的转换表,对于接受两个参数的最简单的调用方式,第一个参数是字符串,表示需要转换的字符,第二个参数也是字符串表示转换的目标。 |
max(str) | 返回字符串 str 中最大的字母。 |
min(str) | 返回字符串 str 中最小的字母。 |
string.partition(str) | 有点像 find()和 split()的结合体,从 str 出现的第一个位置起,把 字 符 串 string 分 成 一 个 3 元 素 的 元 组 (string_pre_str,str,string_post_str),如果 string 中不包含str 则 string_pre_str == string. |
string.replace(str1, str2, num=string.count(str1)) | 把 string 中的 str1 替换成 str2,如果 num 指定,则替换不超过 num 次. |
string.rfind(str, beg=0,end=len(string) ) | 类似于 find()函数,不过是从右边开始查找. |
string.rindex( str, beg=0,end=len(string)) | 类似于 index(),不过是从右边开始. |
string.rjust(width) | 返回一个原字符串右对齐,并使用空格填充至长度 width 的新字符串 |
string.rpartition(str) | 类似于 partition()函数,不过是从右边开始查找. |
string.rstrip() | 删除 string 字符串末尾的空格. |
string.split(str=””, num=string.count(str)) | 以 str 为分隔符切片 string,如果 num有指定值,则仅分隔 num 个子字符串 |
string.splitlines(num=string.count(‘\n’)) | 按照行分隔,返回一个包含各行作为元素的列表,如果 num 指定则仅切片 num 个行. |
string.startswith(obj, beg=0,end=len(string)) | 检查字符串是否是以 obj 开头,是则返回 True,否则返回 False。如果beg 和 end 指定值,则在指定范围内检查. |
string.strip([obj]) | 在 string 上执行 lstrip()和 rstrip() |
string.swapcase() | 翻转 string 中的大小写 |
string.title() | 返回”标题化”的 string,就是说所有单词都是以大写开始,其余字母均为小写(见 istitle()) |
string.translate(str, del=””) | 根据 str 给出的表(包含 256 个字符)转换 string 的字符,要过滤掉的字符放到 del 参数中 |
string.upper() | 转换 string 中的小写字母为大写 |
string.zfill(width) | 返回长度为 width 的字符串,原字符串 string 右对齐,前面填充0 |
string.isdecimal() | isdecimal()方法检查字符串是否只包含十进制字符。这种方法只存在于unicode对象。 |
####前言 — 本文其实讲的不是跨平台的事情,所以标题有点忽悠。。。Duang~Duang~Duang~…我其实是想透过本文,理解下cocos2d-x,他是怎么干活的。好了,下面就开始吧…以android代码为例子…
####Cocos2dxActivity的oncreate —
android世界的起点在activity,cocos2d-x起点也就在Cocos2dxActivity。而oncreate方法就是这个起点的入口。
Cocos2dxActivity的入口方法oncreate
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
onLoadNativeLibraries();//这句话是去加载.so文件。主要代码是System.loadLibrary(libName)
sContext = this;
this.mHandler = new Cocos2dxHandler(this);//初始化Cocos2dxHandler句柄,这个类做的主要工作是负责弹出Dialog
Cocos2dxHelper.init(this);//初始化如包名,apk安装路径,音乐
this.mGLContextAttrs = getGLContextAttrs();//getGLContextAttrs是一个Native方法,具体实现在C++部分,返回的是一个int数组,将返回的值设置到setEGLConfigChooser中,获取不同depth buffer的Surface
this.init();//主要工作是创建一个GLSurfaceView,并且自定义设置这个Surface的每个channel的depth,同时给这个view设置渲染工具
if (mVideoHelper == null) {
mVideoHelper = new Cocos2dxVideoHelper(this, mFrameLayout);//把mFrameLayout赋值到Cocos2dxVideoHelper中,方便如果需要Video的时候添加到mFrameLayout中
}
if(mWebViewHelper == null){
mWebViewHelper = new Cocos2dxWebViewHelper(mFrameLayout);
}
} > Cocos2dxActivity中getGLContextAttrs的做了啥事情
{
cocos_android_app_init(env, thiz);//这句话就是告诉c++们,准备好了啊,给我初始化AppDelegate,顺便提下这方法就在main.cpp中
cocos2d::Application::getInstance()->initGLContextAttrs(); //初始化完了AppDelegate,就调用AppDelegate的initGLContextAttrs方法,同时将设置的值赋值给GLView中的_glContextAttrs
GLContextAttrs _glContextAttrs = GLView::getGLContextAttrs();//获取上面的赋值
int tmp[6] = {_glContextAttrs.redBits, _glContextAttrs.greenBits, _glContextAttrs.blueBits,
_glContextAttrs.alphaBits, _glContextAttrs.depthBits, _glContextAttrs.stencilBits};//把_glContextAttrs的值保存到int中,传递到java层
jintArray glContextAttrsJava = env->NewIntArray(6);
env->SetIntArrayRegion(glContextAttrsJava, 0, 6, tmp);
return glContextAttrsJava;
}
public void init() {Cocos2dxActivity中this.init()方法,具体实现
// FrameLayout
ViewGroup.LayoutParams framelayout_params =
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
mFrameLayout = new FrameLayout(this);//创建一个FrameLayout
mFrameLayout.setLayoutParams(framelayout_params);
// Cocos2dxEditText layout
ViewGroup.LayoutParams edittext_layout_params =
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
Cocos2dxEditText edittext = new Cocos2dxEditText(this);
edittext.setLayoutParams(edittext_layout_params);
// ...add to FrameLayout
mFrameLayout.addView(edittext);//将Cocos2dxEditText添加到FrameLayout
// Cocos2dxGLSurfaceView
this.mGLSurfaceView = this.onCreateView();//创建Cocos2dxGLSurfaceView,修改GLSurfaceView的设置。如setEGLConfigChooser(int, int, int, int, int, int) 指定red ,green, blue, alpha, depth ,stencil 支持的位数,缺省为RGB_565 ,16 bit depth buffer.
// ...add to FrameLayout
mFrameLayout.addView(this.mGLSurfaceView);//将Cocos2dxGLSurfaceView添加到mFrameLayout
// Switch to supported OpenGL (ARGB888) mode on emulator
if (isAndroidEmulator())//如果是模拟器,则重新设置
this.mGLSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
this.mGLSurfaceView.setCocos2dxRenderer(new Cocos2dxRenderer());//设置渲染,主要渲染工作就在Cocos2dxRenderer类中
this.mGLSurfaceView.setCocos2dxEditText(edittext);
// Set framelayout as the content view
setContentView(mFrameLayout);//将FrameLayout添加到到View中
}
public Cocos2dxGLSurfaceView onCreateView() {Cocos2dxActivity中onCreateView()方法具体实现,创建GLSurfaceView
Cocos2dxGLSurfaceView glSurfaceView = new Cocos2dxGLSurfaceView(this);//创建Cocos2dxGLSurfaceView
//this line is need on some device if we specify an alpha bits
if(this.mGLContextAttrs[3] > 0) //GLSurfaceView 缺省创建为RGB_565 颜色格式的Surface ,如果需要支持透明度,可以调用getHolder().setFormat(PixelFormat.TRANSLUCENT).
glSurfaceView.getHolder().setFormat(PixelFormat.TRANSLUCENT);
Cocos2dxEGLConfigChooser chooser = new Cocos2dxEGLConfigChooser(this.mGLContextAttrs);
glSurfaceView.setEGLConfigChooser(chooser);//指定red ,green, blue, alpha, depth ,stencil 支持的位数,缺省为RGB_565 ,16 bit depth buffer.必须在setRenderer()方法之前设置
return glSurfaceView;
} >话说在创建Cocos2dxGLSurfaceView的时候,初始化主要做了啥工作呢?
this.setFocusableInTouchMode(true);//设置GLSurfaceView可以获取触摸事件
@Override在上面的一系列方法中,我们已经创建了GLSurfaceView,但还需要通过Cocos2dxRenderer绘制东西到GLSurfaceView上面。Cocos2dxRenderer实现了GLSurfaceView.Renderer这个接口,具体方法如下:
public void onSurfaceCreated(final GL10 GL10, final EGLConfig EGLConfig) {
Cocos2dxRenderer.nativeInit(this.mScreenWidth, this.mScreenHeight);//这个方法做了好多的工作,稍候具体详解
this.mLastTickInNanoSeconds = System.nanoTime();
mNativeInitCompleted = true;
}
@Override
public void onSurfaceChanged(final GL10 GL10, final int width, final int height) {
Cocos2dxRenderer.nativeOnSurfaceChanged(width, height);//surface 的尺寸发生改变时该方法被调用
}
@Override
public void onDrawFrame(final GL10 gl) {
/*
* No need to use algorithm in default(60 FPS) situation,
* since onDrawFrame() was called by system 60 times per second by default.
*/
if (sAnimationInterval <= 1.0 / 60 * Cocos2dxRenderer.NANOSECONDSPERSECOND) {
Cocos2dxRenderer.nativeRender();//渲染工作,会一直回调
} else {
final long now = System.nanoTime();
final long interval = now - this.mLastTickInNanoSeconds;
if (interval < Cocos2dxRenderer.sAnimationInterval) {
try {
Thread.sleep((Cocos2dxRenderer.sAnimationInterval - interval) / Cocos2dxRenderer.NANOSECONDSPERMICROSECOND);
} catch (final Exception e) {
}
}
/*
* Render time MUST be counted in, or the FPS will slower than appointed.
*/
this.mLastTickInNanoSeconds = System.nanoTime();
Cocos2dxRenderer.nativeRender();
}
}
当SurfaceView创建的时候,会去调用onSurfaceCreated,而我们关心的就是Cocos2dxRenderer.nativeInit这个方法
void Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeInit(JNIEnv* env, jobject thiz, jint w, jint h)
{
auto director = cocos2d::Director::getInstance();//实例化导演类
auto glview = director->getOpenGLView();//获取glview
if (!glview)
{
glview = cocos2d::GLViewImpl::create("Android app");//GLViewImpl这个类是GLView的子类,在不同的平台有不同的实现
glview->setFrameSize(w, h);//设置屏幕大小
director->setOpenGLView(glview);//把glview赋值给导演类
//cocos_android_app_init(env, thiz);
cocos2d::Application::getInstance()->run();//这个方法其实主要就做了就是回调applicationDidFinishLaunching()这个方法,而这个方法其实对于我们开发者来说相当熟悉
}
else//重新初始化各参数
{
cocos2d::GL::invalidateStateCache();
cocos2d::GLProgramCache::getInstance()->reloadDefaultGLPrograms();
cocos2d::DrawPrimitives::init();
cocos2d::VolatileTextureMgr::reloadAllTextures();
cocos2d::EventCustom recreatedEvent(EVENT_RENDERER_RECREATED);
director->getEventDispatcher()->dispatchEvent(&recreatedEvent);
director->setGLDefaultValues();
}
} > 接下来就得说说onDrawFrame中的这方法Cocos2dxRenderer.nativeRender()
JNIEXPORT void JNICALL Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeRender(JNIEnv* env) {
cocos2d::Director::getInstance()->mainLoop();//这方法肯定又是很熟悉,全都跑到Director上了,当然这方法的实现是DisplayLinkDirector这类上方法。每一次回调onDrawFrame,真正运行的就是mainLoop()方法。
} > 还是继续扯下mainLoop()方法
void DisplayLinkDirector::mainLoop()
{
if (_purgeDirectorInNextLoop)//这值默认都是false,只有运行了Director::end()才被设置为true,可见要结束游戏,真正的关键是这
{
_purgeDirectorInNextLoop = false;
purgeDirector();//根据不同的平台运行GLViewImpl::end(),在android中调用的是terminateProcessJNI,而这个方法就是通过调用jni,回调java层实现的方法terminateProcess(),杀掉当前游戏的进程
}
else if (_restartDirectorInNextLoop)//当重新执行Director的时候_restartDirectorInNextLoop为true
{
_restartDirectorInNextLoop = false;
restartDirector();
}
else if (! _invalid)//默认为false,当运行DisplayLinkDirector::stopAnimation()方法为true
{
drawScene();//绘制场景了
// release the objects
PoolManager::getInstance()->getCurrentPool()->clear();//释放AutoreleasePool
}
}
好了,差不多也讲完主要流程了,现在总结下oncreate()这方法主要干了啥?
以上的方法讲的一系列都是从oncreate中引申出来的,下面讲讲onResume(),onPause()方法
####Cocos2dxActivity的onResume和onPause —
//Cocos2dxActivity的两个方法
@Override
protected void onResume() {//再一次进去
super.onResume();
Cocos2dxHelper.onResume();//Cocos2dxHelper.sCocos2dxAccelerometer.enable();
this.mGLSurfaceView.onResume();//GLSurfaceView中resume一系列工作,具体看下列方法
}
@Override
protected void onPause() {//启动Cocos2dxHelper.sCocos2dxAccelerometer.enable();
super.onPause();
Cocos2dxHelper.onPause();//暂停Cocos2dxHelper.sCocos2dxAccelerometer.disable();
this.mGLSurfaceView.onPause();//GLSurfaceView中pause一系列工作,具体看下列方法
}
public void onResume() {
super.onResume();
this.setRenderMode(RENDERMODE_CONTINUOUSLY);//设置渲染模式,不停地渲染。默认是 RENDERMODE_CONTINUOUSLY
this.queueEvent(new Runnable() {
@Override
public void run() {
Cocos2dxGLSurfaceView.this.mCocos2dxRenderer.handleOnResume();//处理Resume操作, Cocos2dxHelper.onEnterForeground();Cocos2dxRenderer.nativeOnResume();//c++中实现
}
});
}
@Override
public void onPause() {
this.queueEvent(new Runnable() {
@Override
public void run() {
Cocos2dxGLSurfaceView.this.mCocos2dxRenderer.handleOnPause();//处理暂停操作
}
});
this.setRenderMode(RENDERMODE_WHEN_DIRTY);//设置渲染模式,根据需要来渲染,当运行requestRender()方法之后渲染
//super.onPause();
}
this.queueEvent方法是将一个线程放到ArrayList列表中,在GLThread线程中,会将ArrayList列表中的线程取出来运行,GLThread线程可以和GLSurfaceView渲染界面交互信息。Cocos2dxActivity中的runOnGLThread方法就是封装的queueEvent()方法。
上面一系列讲解了关于在android平台中,cocos2d-x是怎么开展工作的,其实也很简单。就是要创建一个GLSurfaceView,而GLSurfaceView的创建需要设置一些参数,比如setEGLConfigChooser(red ,green, blue, alpha, depth ,stencil),表示这个View,每一个渲染点的支持的位数,其次就是创建了GLSurfaceView了,那需要在view上面绘制东西啊,就需要Cocos2dxRenderer,他一直调用onDrawFrame方法,最终就有东西了。
本文其实没有讲到怎么跨平台,但是其实不同的平台,都差不多。都需要一个View,再在上面不停的绘制东西,而真正绘制的实现在平台不相关代码上。
]]>####JNIEnv*环境变量的获取 —
JNIEnv* JniHelper::getEnv() {
JNIEnv *_env = (JNIEnv *)pthread_getspecific(g_key);
if (_env == nullptr)
_env = JniHelper::cacheEnv(_psJavaVM);
return _env;
}
####jstring以及std::string之间的转换 —
jstring转成std::string
std::string JniHelper::jstring2string(jstring jstr)
{
if (jstr == nullptr) {
return "";
}
JNIEnv *env = JniHelper::getEnv();
if (!env) {
return nullptr;
}
const char* chars = env->GetStringUTFChars(jstr, nullptr);//将jstring转成char*
std::string ret(chars);
env->ReleaseStringUTFChars(jstr, chars);//删除引用
return ret;
} ---
std::string转成jstring
JNIEnv* env = cocos2d::JniHelper::getEnv();
jstring _jstrClassName = env->NewStringUTF(className);//将std::string转成jstring ---
jstringtostring相互转换的例子
public static String getItem(String key) {//Cocos2dxLocalStorage.java类中的方法,传入一个key
return ret == null ? "" : ret;//返回String
}
//下面是在c++中回调这个方法
std::string localStorageGetItem( const std::string& key )
{
assert( _initialized );
JniMethodInfo t;//一个struct含有JNIEnv * ,jclass,jmethodID属性
std::string ret;
if (JniHelper::getStaticMethodInfo(t, "org/cocos2dx/lib/Cocos2dxLocalStorage", "getItem", "(Ljava/lang/String;)Ljava/lang/String;")) {//获取getItem类id,以及方法id
jstring jkey = t.env->NewStringUTF(key.c_str());//将std::string转成jstring
jstring jret = (jstring)t.env->CallStaticObjectMethod(t.classID, t.methodID, jkey);//传入获取的类id,方法id,回调getItem方法,返回jstring
ret = JniHelper::jstring2string(jret);//jstring转成std::string
t.env->DeleteLocalRef(jret);//删除引用
t.env->DeleteLocalRef(jkey);//删除引用
t.env->DeleteLocalRef(t.classID);//删除引用
}
return ret;
} --- <a id='getMethodInfo' name='getMethodInfo'> </a> ####[获取方法的信息](#top) ---
获取静态方法信息static bool getStaticMethodInfo(JniMethodInfo &methodinfo,
const char *className,
const char *methodName,
const char *paramCode);
获取常规方法信息static bool getMethodInfo(JniMethodInfo &methodinfo,
const char *className,
const char *methodName,
const char *paramCode);
className表示的是包名+类名,methodName表示的是调用的方法名,paramCode表示的是传入参数以及返回值类型,它的格式为:(参数)返回类型 ,不管是常规还是静态方法最终都把相关信息保存在methodinfo中.
paramCode参数类型书写格式对应表
参数类型 | 参数简写 |
---|---|
boolean | Z |
byte | B |
char | C |
short | S |
int | I |
long | J |
float | F |
double | D |
void | V |
Object | Ljava/lang/String; |
Array | [Ljava/lang/String; |
注意Object以及Array两种类型之后的分号。示例:(IIII)V //表示传入参数是4个整数,返回值为void。ILjava/lang/String;I //表示的是传入参数为(int,string,int)
####C++回调JAVA的静态方法以及普通方法 —
JNIEvn*有一系列的CallStatic[返回类型]Method、Call[返回类型]Method方法,针对不同的函数返回类型选择性调用。
- CallStaticVoidMethod ———void返回类型
- CallVoidMethod ———void返回类型
函数名 | 函数返回值类型 |
---|---|
Void | void |
Object | jobject |
Boolean | jboolean |
Byte | jbyte |
Char | jchar |
Short | jshort |
Int | jint |
Long | jlong |
Float | jfloat |
Double | jdouble |
- jniMethodInfo.env->CallVoidMethod(jobject jobj,jmethodID methodId,..需要传入的参数..)
- jniMethodInfo.env->CallStaticObjectMethod(jclass jclassid,jmethodID methodId,..需要传入的参数..)
- 以上两个方法最主要的区别在于第一个参数的不同,普通方法传入的是jobject,而静态方法传入的是jclass。在调用普通Method的时候必须先通过方法
jniMethodInfo.env->CallStaticObjectMethod(jniMethodInfo.classID, jniMethodInfo.methodID,...);
获取jobject实例,至于静态方法则不需要那么麻烦。
普通方法回调例子
bool isHave = JniHelper::getStaticMethodInfo(minfoDeviceUuid,"包名","getInstance","()Lcom/u9time/buyumalatang/activity/NewArcadeFishActivity;");
jobject jobj;//获取实例
if (isHave) {
jobj = minfoDeviceUuid.env->CallStaticObjectMethod(minfoDeviceUuid.classID, minfoDeviceUuid.methodID);//调用CallStaticObjectMethod方法获取实例
bool isHaveUuid = JniHelper::getMethodInfo(minfoDeviceUuid, "com/u9time/buyumalatang/activity/NewArcadeFishActivity", "showSubWebView", "(Ljava/lang/String;Ljava/lang/String;)V");//获取普通方法信息
if(isHaveUuid){
jstring strUrl = str2jstring(minfoDeviceUuid.env, CCUserDefault::sharedUserDefault()->getStringForKey("AU").c_str());
jstring strWebviewType = str2jstring(minfoDeviceUuid.env, "protocol");
minfoDeviceUuid.env->CallVoidMethod(jobj, minfoDeviceUuid.methodID, strUrl, strWebviewType);//回调普通方法
}
} ---
静态方法回调例子
void localStorageSetItem( const std::string& key, const std::string& value)
{
assert( _initialized );
JniMethodInfo t;
if (JniHelper::getStaticMethodInfo(t, "org/cocos2dx/lib/Cocos2dxLocalStorage", "setItem", "(Ljava/lang/String;Ljava/lang/String;)V")) {//获取setItem方法信息
jstring jkey = t.env->NewStringUTF(key.c_str());
jstring jvalue = t.env->NewStringUTF(value.c_str());
t.env->CallStaticVoidMethod(t.classID, t.methodID, jkey, jvalue);//回调方法
t.env->DeleteLocalRef(jkey);
t.env->DeleteLocalRef(jvalue);
t.env->DeleteLocalRef(t.classID);
}
}
####java回调c++实现的方法 —
没有返回值的回调
private static native void nativeInsertText(final String text);
extern "C" {
JNIEXPORT void JNICALL Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeInsertText(JNIEnv* env, jobject thiz, jstring text) {//注意命名,以及方法包含在extern "C"中
const char* pszText = env->GetStringUTFChars(text, NULL);
cocos2d::IMEDispatcher::sharedDispatcher()->dispatchInsertText(pszText, strlen(pszText));
env->ReleaseStringUTFChars(text, pszText);
}
}
含有返回值的回调
private static native int[] getGLContextAttrs();
extern "C" {
jintArray Java_org_cocos2dx_lib_Cocos2dxActivity_getGLContextAttrs(JNIEnv* env, jobject thiz)
{
cocos_android_app_init(env, thiz);
cocos2d::Application::getInstance()->initGLContextAttrs();
GLContextAttrs _glContextAttrs = GLView::getGLContextAttrs();
int tmp[6] = {_glContextAttrs.redBits, _glContextAttrs.greenBits, _glContextAttrs.blueBits,
_glContextAttrs.alphaBits, _glContextAttrs.depthBits, _glContextAttrs.stencilBits};
jintArray glContextAttrsJava = env->NewIntArray(6);
env->SetIntArrayRegion(glContextAttrsJava, 0, 6, tmp);
return glContextAttrsJava;
}
}
####创建删除Program
—
GLuint glCreateProgram(void)
这个函数没输入参数,它返回新建项目的句柄
void glDeleteProgram(GLuint program)
program :要删除项目的句柄 着色器在使用的时候,需要链接到一个容器中,这个容器称之为着色器程序容器。通过`glCreateProgram`创建这个容器,返回这个容器的句柄。
####链接着色器到Program
—
void glAttachShader(GLuint program, GLuint shader)
program :项目的句柄
shader :着色器句柄
将着色器对象shader关联到着色器程序program上。着色器对象可以在任何时候关联到着色器程序,但是它的功能只有经过程序的成功链接之后才是可用的。着色器对象可以同时关联到多个不同的着色器程序上,一个程序只能有一个顶点着色器和一个片元着色器。
####从Program中解除着色器
—
void glDetachShader(GLuint program, GLuint shader)
program :项目的句柄
shader :着色器句柄
如果我们需要从程序中移除一个着色器对象,从而改变着色器的操作,那么可以调用glDetachShader()函数,设置对应的着色器对象标识符来解除对象的关联
####opengles出错处理
—
GLenum glGetError(void)
返回当前错误码,并将错误码复位成GL_NO_ERROR,如果返回GL_NO_ERROR,说明自从上次查询后,没有错误产生。
| 错误码 | 描述 |
|:————-: |:—————:|
|GL_NO_ERROR
| 没错误产生自从上次 glGetError()调用后 |
| GL_INVALID_ENUM
| 枚举值超出枚举范围,错误被忽略 |
| GL_INVALID_VALUE
|数值超出枚举范围,错误被忽略 |
|GL_INVALID_OPERATION
| 命令不能被执行,在当前环境下,错误被忽略 |
| GL_OUT_OF_MEMORY
| 没有足够内存执行命令,如果错误产生,OpenGL ES的管线状态不确定|
OpenGL ES命令出错会产生一个错误码,错误码被记录,能够使用glGetError
函数查询,在第一个被记录的错误码被查询前,不会记录新的错误码。一旦错误码被查询,当前错误码将变成GL_NO_ERROR
。除了GL_OUT_OF_MEMORY
错误码以外,其它的错误码将被忽略, 不影响程序运行状态。
####cocos2d-x中着色器绑定到Program流程 —
bool GLProgram::initWithByteArrays(const GLchar* vShaderByteArray, const GLchar* fShaderByteArray)
{
_program = glCreateProgram();//创建Program
CHECK_GL_ERROR_DEBUG();//宏定义封装了glGetError
_vertShader = _fragShader = 0;//顶点着色器以及片元着色器句柄初始化
if (vShaderByteArray)//如果顶点着色器源码不为null
{
if (!compileShader(&_vertShader, GL_VERTEX_SHADER, vShaderByteArray))//编译顶点着色器源码,同时赋值了顶点着色器句柄,在上文中已经详解了编译着色器内容
{
CCLOG("cocos2d: ERROR: Failed to compile vertex shader");
return false;
}
}
if (fShaderByteArray)//如果片元着色器源码不为null
{
if (!compileShader(&_fragShader, GL_FRAGMENT_SHADER, fShaderByteArray)) 编译片元着色器源码,同时赋值了片元着色器句柄,在上文中已经详解了编译着色器内容
{
CCLOG("cocos2d: ERROR: Failed to compile fragment shader");
return false;
}
}
if (_vertShader)//顶点着色器句柄不为0
{
glAttachShader(_program, _vertShader);//将顶点着色器句柄链接到Program中
}
CHECK_GL_ERROR_DEBUG(); //宏定义封装了glGetError
if (_fragShader)//片元着色器句柄不为0
{
glAttachShader(_program, _fragShader); //将片元着色器句柄链接到Program中
}
_hashForUniforms.clear();//compileShader是个std::unordered_map<GLint, GLvoid*>类型的map,它的主要功能是通过比较统一变量的location,以及统一变量的值来判断是否需要向着色器传递相应的值。主要方法涉及到updateUniformLocation,setUniformLocationWithMatrix4fv等
CHECK_GL_ERROR_DEBUG();
return true;
}
####生成可执行程序
—
void glLinkProgram(GLuint program)
program :项目的句柄
通过调用glLinkProgram
方法,链接生成一个完整的着色器可执行程序。
####查询Program的相关状态
—
void glGetProgramiv(GLuint program, GLenum pname, GLint *params)
program :program句柄
pname :查询条件
params :返回结果
通过glGetProgramiv
方法可以检查链接是否成功. 当参数为GL_LINK_STATUS
。如果返回GL_TRUE
,那么链接成功,否则返回GL_FALSE
相关参数:
GL_ACTIVE_ATTRIBUTES
–当前正在被项目使用的属性数量GL_ACTIVE_ATTRIBUTE_MAX_LENGTH
–当前正在被项目使用的属性最长字段长度GL_ACTIVE_UNIFORMS
–当前正在被项目使用的一致变量GL_ACTIVE_UNIFORM_MAX_LENGTH
–当前正在被项目使用的一致变量最长字段长度GL_ATTACHED_SHADERS
–当前正在被项目使用的着色器数量GL_DELETE_STATUS
–The GL_DELETE_STATUS
query returns whether a program object has been marked for deletionGL_INFO_LOG_LENGTH
– As with shader objects, program objects store an info log, the length of which can be queried for using GL_INFO_LOG_LENGTH
.GL_LINK_STATUS
–To check whether a link was successful, you can query for GL_LINK_STATUS
.GL_VALIDATE_STATUS
–检查项目当前的执行状态,当然也可以通过glValidateProgram
方法检测渲染是否成功,如果你使用glValidateProgram
仅仅为了调试,这将是缓慢的,不能在渲染前随时使用。 如果你的应用成功渲染,就不要使用它了####绑定顶点属性索引到属性变量 —
void glBindAttribLocation(GLuint program, GLuint index, const GLchar *name)
program :项目句柄
index :一般的顶点属性索引
name :属性变量名字
顶点着色器中使用glBindAttribLocation
绑定一个普通的顶点属性索引到属性变量, 这个绑定在项目下次链接时有效,在当前链接的项目里不会改变.通过glBindAttribLocation
方法需要我们预先设置一个索引值和变量绑定
GLint glGetAttribLocation(GLuint program,const GLchar *name)
program :项目句柄 name :属性变量名字
glGetAttribLocation
返回一个绑定属性变量名字的普通属性索引.opengles会自动绑定所有attributes,外部程序只需通过glGetAttribLocation
获取指定attribute名的index.当然如果当项目句柄不是一个有效的引用或者在绑定到项目中的顶点着色器中没有指定attribute的名字,则返回-1,表明是一个无效的属性索引。
上诉两个方法都可以将顶点属性索引绑定到属性变量中。一种是自动绑定,我们获取绑定的索引,另外一种是通过事先设置的索引绑定。当然在通过glBindAttribLocation
绑定后,再调用glGetAttribLocation
方法则是查询相应的attributes的属性索引,而不是重新自动绑定。
####查询属性的信息
—
void glGetActiveAttrib(GLuint program, GLuint index,GLsizei bufsize, GLsizei *length, GLint *size, GLenum *type, GLchar *name)
program :项目句柄
index :指定需要查询的顶点属性,值是0到GL_ACTIVE_ATTRIBUTES-1。GL_ACTIVE_ATTRIBUTES值使用glGetProgramiv(_program, GL_ACTIVE_ATTRIBUTES, &activeAttributes)确定
bufsize :specifies the maximum number of characters that may be written into name, including the null terminator.
length :returns the number of characters written into name, excluding the null terminator, if length is not NULL.
size :返回属性的大小,指定返回单元类型的单位。如果不是数组,应该是1;如果是数组,返回数组的尺寸
type :returns the type of the attribute. Valid values are GL_FLOAT GL_FLOAT_VEC2 GL_FLOAT_VEC3 GL_FLOAT_VEC4 GL_FLOAT_MAT2 GL_FLOAT_MAT3
...
name :name of the attribute variable as declared in the vertex shader.
####获取program的log信息
—
void glGetProgramInfoLog(GLuint program, GLsizei maxLength, GLsizei *length,
GLchar *infoLog)
program :程序的句柄
maxLength :the size of the buffer in which to store the info log
length :the length of the info log written (minus the null terminator); if the length does not need to be known,this parameter can be NULL
infoLog :pointer to the character buffer in which to store the info log
在调用glGetProgramInfoLog
之前,一般都调用glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength)
方法,事先获取log长度。
####获取一致变量的索引
—
GLint glGetUniformLocation(GLuint program, const GLchar* name)
program :handle to the program object
name :the name of the uniform for which to get the location
glGetUniformLocation
方法将返回被这个一致变量名字绑定的索引,如果该变量在着色器中没有找到,将返回-1.
####查询一致变量的信息
—
void glGetActiveUniform(GLuint program, GLuint index, GLsizei bufSize, GLsizei *length,GLint *size, GLenum *type, GLchar *name)
program :程序的句柄
index :the uniform index to be queried
bufSize :the number of characters in the name array
length :if not NULL, will be written with the number of characters written into the name array (less the null terminator)
size :if the uniform variable being queried is an array, this variable will be written with the maximum array element used in the program (plus 1); if the uniform variable being queried is not an array, this value will be 1
type :will be written with the uniform type; can be GL_FLOAT, GL_FLOAT_VEC2, GL_FLOAT_VEC3,GL_FLOAT_VEC4, GL_INT, GL_INT_VEC2, GL_INT_VEC3,GL_INT_VEC4, GL_UNSIGNED_INT,GL_UNSIGNED_INT_VEC2, GL_UNSIGNED_INT_VEC3,GL_UNSIGNED_INT_VEC4, GL_BOOL, GL_BOOL_VEC2,GL_BOOL_VEC3, GL_BOOL_VEC4, GL_FLOAT_MAT2...
name :will be written with the name of the uniform up to bufSize number of characters; this will be a null-terminated string
glGetActiveUniform()
方法和glGetActiveAttrib
类似,返回变量的相关信息
####cocos2d-x中Program的link —
GLProgram::link()
方法具体实现解析
bool GLProgram::link()
{
CCASSERT(_program != 0, "Cannot link invalid program");//断言判断program是否可用
GLint status = GL_TRUE;
bindPredefinedVertexAttribs();//将预先设置的属性索引绑定到属性名字上,具体调用glBindAttribLocation
glLinkProgram(_program);//链接生成一个完整的着色器可执行程序
parseVertexAttribs();//解析顶点着色器的属性
parseUniforms();//解析着色器的一致变量
if (_vertShader)//如果着色器正连接着一个项目对象,glDeleteShader不会立刻删除着色器,而是设置删除标记,一旦着色器不再连接项目对象,才删除着色器使用的内存
{
glDeleteShader(_vertShader);
}
if (_fragShader)
{
glDeleteShader(_fragShader);
}
_vertShader = _fragShader = 0;
return (status == GL_TRUE);
}
GLProgram::bindPredefinedVertexAttribs()
方法具体实现
void GLProgram::bindPredefinedVertexAttribs()
{
static const struct {
const char *attributeName;//属性的名字
int location;//属性的索引
} attribute_locations[] =
{
{GLProgram::ATTRIBUTE_NAME_POSITION, GLProgram::VERTEX_ATTRIB_POSITION},//POSITION属性
{GLProgram::ATTRIBUTE_NAME_COLOR, GLProgram::VERTEX_ATTRIB_COLOR},//COLOR属性
{GLProgram::ATTRIBUTE_NAME_TEX_COORD, GLProgram::VERTEX_ATTRIB_TEX_COORD},//纹理shux
{GLProgram::ATTRIBUTE_NAME_TEX_COORD1, GLProgram::VERTEX_ATTRIB_TEX_COORD1},
{GLProgram::ATTRIBUTE_NAME_TEX_COORD2, GLProgram::VERTEX_ATTRIB_TEX_COORD2},
{GLProgram::ATTRIBUTE_NAME_TEX_COORD3, GLProgram::VERTEX_ATTRIB_TEX_COORD3},
{GLProgram::ATTRIBUTE_NAME_NORMAL, GLProgram::VERTEX_ATTRIB_NORMAL},//法线属性
};
const int size = sizeof(attribute_locations)/sizeof(attribute_locations[0]);
for(int i=0; i<size;i++) {
glBindAttribLocation(_program, attribute_locations[i].location, attribute_locations[i].attributeName);//把属性名字绑定到索引上
}
}
parseVertexAttribs
方法实现
void GLProgram::parseVertexAttribs()
{
_vertexAttribs.clear();//清空存储顶点属性的unordered_map
GLint activeAttributes;//属性的个数
GLint length;//最长属性名字的长度
glGetProgramiv(_program, GL_ACTIVE_ATTRIBUTES, &activeAttributes);//获取属性的总个数
if(activeAttributes > 0)
{
VertexAttrib attribute;
glGetProgramiv(_program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &length);//获取最长属性名字的长度
if(length > 0)
{
GLchar* attribName = (GLchar*) alloca(length + 1);//开辟一片空间保存属性名字
for(int i = 0; i < activeAttributes; ++i)//通过迭代activeAttributes,遍历查询属性信息。从0至GL_ACTIVE_ATTRIBUTES-1
{
// 查询属性信息
glGetActiveAttrib(_program, i, length, nullptr, &attribute.size, &attribute.type, attribName);//注意传入参数i,以及返回的size,type,name
attribName[length] = '\0';
attribute.name = std::string(attribName);//保存属性的名字
// Query the pre-assigned attribute location
attribute.index = glGetAttribLocation(_program, attribName);//通过名字获取属性索引,注意比较前面的glBindAttribLocation索引
_vertexAttribs[attribute.name] = attribute;//将获取的属性保存到map中
}
}
}
else
{
GLchar ErrorLog[1024];
glGetProgramInfoLog(_program, sizeof(ErrorLog), NULL, ErrorLog);//获取program的info
CCLOG("Error linking shader program: '%s'\n", ErrorLog);
}
}
GLProgram::parseUniforms()
具体实现
void GLProgram::parseUniforms()
{
_userUniforms.clear();//清空保存一致变量的map
GLint activeUniforms;//一致变量在着色器中的数量
glGetProgramiv(_program, GL_ACTIVE_UNIFORMS, &activeUniforms);//获取着色器中一致变量的总数
if(activeUniforms > 0)
{
GLint length;//最长一致变量的名字长度
glGetProgramiv(_program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &length);//获取最长一致变量的名字长度
if(length > 0)
{
Uniform uniform;
GLchar* uniformName = (GLchar*)alloca(length + 1);//开辟一片空间保存一致变量名字
for(int i = 0; i < activeUniforms; ++i)//迭代
{
// Query uniform info.
glGetActiveUniform(_program, i, length, nullptr, &uniform.size, &uniform.type, uniformName);//注意传入的i,以及size,type,name
uniformName[length] = '\0';
//当不是以CC_开头的一致遍历的时候保存到map中
if(strncmp("CC_", uniformName, 3) != 0) {
// remove possible array '[]' from uniform name
if(length > 3)
{
char* c = strrchr(uniformName, '[');
if(c)
{
*c = '\0';
}
}
uniform.name = std::string(uniformName);
uniform.location = glGetUniformLocation(_program, uniformName);//获取相应名字的索引
GLenum __gl_error_code = glGetError();
if (__gl_error_code != GL_NO_ERROR)
{
CCLOG("error: 0x%x", (int)__gl_error_code);
}
assert(__gl_error_code == GL_NO_ERROR);
_userUniforms[uniform.name] = uniform;
}
}
}
}
else
{
GLchar ErrorLog[1024];
glGetProgramInfoLog(_program, sizeof(ErrorLog), NULL, ErrorLog);//programe的log信息
CCLOG("Error linking shader program: '%s'\n", ErrorLog);
}
}
以上就是关于cocos中着色器在program的创建链接,以及对于相应属性的编译,以及错误的处理的大体流程
####Reference —
]]>
####创建着色器
—
GLuint glCreateShader(GLenum type)
type:着色器的类型 GL_VERTEX_SHADER或者GL_FRAGMENT_SHADER.
使用glCreateShader
你能依据输入参数创建一个顶点着色器或片段着色器,返回值是个新着色器的句柄,是一个非零的整数值,如果为0则说明发生了错误。不使用了,用glDeleteShader
删除它。
void glDeleteShader(GLuint shader)
shader:要删除的着色器句柄.
如果着色器正连接着一个项目对象,glDeleteShader
不会立刻删除着色器,而是设置删除标记,一旦着色器不再连接项目对象,才删除着色器使用的内存。
####加载着色器源码 —
void glShaderSource(GLuint shader, GLsizei count,const char** string,const GLint* length)
shader :着色器对象句柄
count :着色器源码的字符数,着色器由字符串组成,每个着色器仅有一个主函数main
string :一个由count行GLchar类型的字符串组成的数组,用来表示着色器的源代码数据,string中的字符串可以是NULL结尾的,也可以不是
length :可以是以下三种值的一种。如果length是NULL,那么我们假设string给出的每行字符串都是NULL结尾的。否则,length中必须有count个元素,它们分别表示string中对应行的长度。如果length数组中的某个值是一个整数,那么它表示对应的字符串中的字符数。如果某个值是负数,那么string中的对应行假设为NULL结尾
####编译着色器 —
void glCompileShader(GLuint shader)
shader :着色器对象句柄
####获取编译状态 —
void glGetShaderiv(GLuint shader, GLenum pname,GLint *params)
shader :着色器句柄
pname :需要查询的信息.GL_COMPILE_STATUS|GL_DELETE_STATUS|GL_INFO_LOG_LENGTH|GL_SHADER_SOURCE_LENGTH|GL_SHADER_TYPE
Params :返回查询结果的整型指针
使用GL_COMPILE_STATUS
参数查询编译是否成功,成功返回GL_TRUE
,失败返回GL_FALSE
。如失败,编译错误被写入 info日志。info日志记录了编译器的任何错误和警告。 即使编译成功,同样也有info日志,可以使用GL_INFO_LOG_LENGTH
查询 info日志长度。 可以调用glGetShaderInfoLog
调用info日志。使用GL_SHADER_TYPE
查询着色器类型是GL_VERTEX_SHADER
或者GL_FRAGMENT_SHADER
.使用GL_SHADER_SOURCE_LENGTH
查询着色器源码长度,即使着色器为空。使用GL_DELETE_STATUS
查询着色器是否被glDeleteShader
标记删除。
####调用info日志 —
void glGetShaderInfoLog(GLuint shader, GLsizei maxLength, GLsizei *length, GLchar *infoLog)
shader :handle to the shader object to get the info log for
maxLength :the size of the buffer to store the info log in
length :the length of the info log written (minus the null terminator). If the length does not need to be known, this parameter can be NULL
infoLog :pointer to the character buffer to store the info log in
要使用glGetShaderInfoLog
调用info日志,首先要通过glGetShaderiv
查询GL_INFO_LOG_LENGTH
,因为只有分配足够的字符串才能存储info log。
####获取着色器源码
—
void glGetShaderSource(GLuint shader, GLsizei bufsize, GLsizei *length, GLchar *source)
shader :指定查询的着色器
bufsize :the number of bytes available in the array source for returning the shader’s source
length :the length of the returned shader string
source :指定存储着着色器源码的GLchar类型数组 --- --- <a id='cocobuildshader' name='cocobuildshader'> </a> ####[cocos2d-x编译着色器示例](#top) --- cocos2d-x编译着色器的代码主要封装在GLProgram类中,下面就来看看GLProgram中的compileShader方法。
bool GLProgram::compileShader(GLuint * shader, GLenum type, const GLchar* source)
{
GLint status;
if (!source)
{
return false;
}
const GLchar *sources[] = {
"uniform mat4 CC_PMatrix;\n"
"uniform mat4 CC_MVMatrix;\n"
"uniform mat4 CC_MVPMatrix;\n"
"uniform mat3 CC_NormalMatrix;\n"
"uniform vec4 CC_Time;\n"
"uniform vec4 CC_SinTime;\n"
"uniform vec4 CC_CosTime;\n"
"uniform vec4 CC_Random01;\n"
"uniform sampler2D CC_Texture0;\n"
"uniform sampler2D CC_Texture1;\n"
"uniform sampler2D CC_Texture2;\n"
"uniform sampler2D CC_Texture3;\n"
"//CC INCLUDES END\n\n",
source,
};
*shader = glCreateShader(type);//创建着色器
glShaderSource(*shader, sizeof(sources)/sizeof(*sources), sources, nullptr);//加载着色器源码
glCompileShader(*shader);//编译着色器源码
glGetShaderiv(*shader, GL_COMPILE_STATUS, &status);//获取编译状态
if (! status)//如果编译失败
{
GLsizei length;
glGetShaderiv(*shader, GL_SHADER_SOURCE_LENGTH, &length);//获取着色器源码长度
GLchar* src = (GLchar *)malloc(sizeof(GLchar) * length);//创建保存源码空间
glGetShaderSource(*shader, length, nullptr, src);//获取着色器源码
CCLOG("cocos2d: ERROR: Failed to compile shader:\n%s", src);//打印出错的编译源码
if (type == GL_VERTEX_SHADER)//顶点着色器
{
CCLOG("cocos2d: %s", getVertexShaderLog().c_str());//打印顶点着色器出错log
}
else//片元着色器
{
CCLOG("cocos2d: %s", getFragmentShaderLog().c_str());//打印片元着色器出错log
}
free(src);//回收保存源码空间
return false;;
}
return (status == GL_TRUE);
} 关于打印顶点或者片元着色器出错log的方法,其实就是上文提到的事先得先通过`glGetShaderiv`查询`GL_INFO_LOG_LENGTH`,获取log的长度,再通过`glGetShaderInfoLog`获取info日志,只不过cocos封装了下,方便调用。
//获取顶点着色器log
std::string GLProgram::getVertexShaderLog() const
{
return this->logForOpenGLObject(_vertShader, (GLInfoFunction)&glGetShaderiv, (GLLogFunction)&glGetShaderInfoLog);//传入的是指向glGetShaderiv方法的指针以及指向glGetShaderInfoLog方法的指针
}
//获取片元着色器log
std::string GLProgram::getFragmentShaderLog() const
{
return this->logForOpenGLObject(_fragShader, (GLInfoFunction)&glGetShaderiv, (GLLogFunction)&glGetShaderInfoLog);
}
//上诉两个方法其实调用的都是同一个方法`logForOpenGLObject`,只不过传入的参数不同,分别是顶点着色器的句柄以及片元着色器的句柄,至于其他两个参数,传的是一样的。
std::string GLProgram::logForOpenGLObject(GLuint object, GLInfoFunction infoFunc, GLLogFunction logFunc) const
{
std::string ret;
GLint logLength = 0, charsWritten = 0;
infoFunc(object, GL_INFO_LOG_LENGTH, &logLength);//回调glGetShaderiv方法,获取log长度
if (logLength < 1)
return "";
char *logBytes = (char*)malloc(logLength);//根据logLength长度开辟一片空间保存log
logFunc(object, logLength, &charsWritten, logBytes);//回调glGetShaderInfoLog获取log内容,保存在logBytes中
ret = logBytes;//将logBytes赋值给ret
free(logBytes);//回收保存log的空间
return ret;
}
####在本文中主要是结合cocos2d-x,回顾下着色器的创建和编译,以及在编译出错的情况下,查询编译状态以及出错信息的处理。。。
####Reference —
]]>