基于区块链的去中心化、不可篡改、共识算法、匿名性与跨平台等特性,区块链+游戏将带来如下变革:
1.去中心化运营
游戏中的各系统设置使用智能合约技术开发,游戏数据存储在区块链上,不依赖中心化服务器;
2.数据可信任
结合区块链技术开发游戏,重要的数据存储在区块链上,游戏运营方无法随意篡改与删除游戏数据,稀有道具内容、数量及抽签概率等算法完全公开,使得游戏数据透明化,可信任化,成为一个可信任的去中心化游戏应用;
智能合约的结构
合约就像一个类(class),其中包含:
状态变量(state variable)
函数(function)
函数修改器(function modifier)
事件(event)
结构(structure)
枚举(enum)
示例:contract Sample
{
//状态变量
uint256 data;
address owner;
//定义事件
event logData(uint256 dataToLog);
//函数修改器
modifier onlyOwner(){
if(msg.sender!=owner)throw;
}
//构造器,名字与合约名一致
function Sample(uint256 initData,address initOwner){
data=initData;
owner=initOwner;
}
//函数
function getData()returns(uint256 returnedData){
return data;
}
function setData()returns(uint256 newData)onlyOwner{
logData(newData);
data=newData;
}
}
代码注释:
contract关键字:用于声明一个合约
data和owner:是两个状态变量。data包含一些数据,owner包含所有者的以太坊钱包地址,即部署合约者的以太坊地址
event logData定义事件logData,用于通知客户端:一旦data发生变化,将触发这个事件。所有事件都保存在区块链中。
函数修改器:onlyOwner。修改器用于在执行一个函数之前自动检测文件。这里的修改器用于检测合约所有者是否在调用函数。如果没有,则会抛出异常。
合约函数构造器constructor:在部署合约时,构造器用于初始化状态变量。
function,getData()用于得到data状态变量的值,setData()用于改变data的值。
基本类型
除了数组类型、字符串类型、结构类型、枚举类型和map类型外,
其他类型均称为基本类型。
无符号型:例如uint8,uint16,uint24,…,uint256分别用于存储无符号的8位,16
位,24位,…,256位整数
有符号型:例如,int8,int16,…,int256分别用于存储8位,16位,24位,…,256位整数
address类型:用于存储以太坊地址,用16进制表示。address类型有两个属性:balance和send。balance用于检测地址余额,send用于向地址发送以太币。send方法拿出需要转账那
些数量的wei,并根据转账是否成功返回true或者false。
注意:
uint和int是uint256和int256的别名。
如果一个数字超过256位,则使用256位数据类型存储该数字的近似值。
数组:Solidity支持generic和byte两种数组类型。
数组有length属性,用于发现数组的长度。
注意:不可以在内存中改变数组大小,也不可以改变非动态数组大小。
字符串类型
有两种方法创建字符串:使用bytes和string。
bytes用于创建原始字符串,而string用于创建UTF-8字符串
示例:
contract sample{
string myString="";//string
bytes myRawString;
function sample(string initString,bytes rawStringInit){
myString=initString;
string storage myString2=myString;
string memory myString3="ABCDE";
myString3="imaginecode";
myRawString=rawStringInit;
myRawString.length++;
}
}
结构类型struct
示例
contract sample{
struct myStruct{
bool myBool;
string myString;
}
myStruct s1;
myStruct s2=myStruct{true,""};
function sample(bool initBool,string initString){
s1=myStruct(initBool,initString);
myStruct memory s3=myStruct(initBool,initString);
}
}
注意:函数参数不可以是结构类型,且函数不可以返回结构类型。
枚举类型enum
示例
contract sample{
enum OS{OSX,Linux,Unix,windows}
OS choice;
function sample(OS chosen){
choice=chosen;
}
function setLinux(){
choice=OS.Linux;
}
function getChoice return(OS chosenOS){
return choice;
}
}
mapping类型
mapping类型只可以存在于storage中,不存在于memory中,因此它们是作为状态变量声明的。
mapping类型包含key/value对,不是实际存储key,而是存储key的keccak256哈希,用于查询value。
mapping不可以被分配给另一个mapping。
constract sample{
mapping(int=>string)myMap;
function sample(){
myMap[key]=value;
mapping(int=>string)myMap2=myMap;
}
}
注意:如果想访问mapping中不存在的key,返回的value为0。
delete操作符
可用于操作任何类型的变量。
对动态数组使用delete操作符,则删除所有元素,其长度变为0。
对静态数组使用delete操作符,则重置所有索引
对map类型使用delete操作符,什么都不会发生,但是,对map类型的一个键使用delete操作符,则会删除与该键相关的值
示例
contract sample{
struct Struct{
mapping(int=>int)myMap;
int myNumber;
}
int[]myArray;
Struct myStruct;
function sample(int key,int value,int number,int[]array){
myStruct=Struct(number);
myStruct=Struct(number);
myStruct.myMap[key]=value;//对某个键赋值
myArray=array;
}
function reset(){
delete myArray;//myArray数组长度为0
delete myStruct;//myNumber为0,myMap不变
}
function deleteKey(int key){
delete myStruct.myMap[key];//删除myMap中的某个键的值
}
}
基本类型之间的转换
隐式转换:常用。通常来说,如果没有语义信息丢失,值和类型之间可以进行隐式转换:uint8可转换为uint16,int128可转换为int256,但是int8不可转换为uint256(因为uint256不能存储,例如-1)
Solidity也支持显式转换,如果编译器不允许在两种数据类型之间隐式转换,则可以进行显式转换。建议尽量避免显式转换,因为可能返回难以预料的结果。
示例:
uint32 a=0x12345678;
uint16 b=uint16(a);//b=0x5678,将uint32类型显式转换为uint16,也就是说,把较大类型转换为较小类型,因此高位被截掉了
var
使用关键字var声明的变量,其变量类型根据分配给它的第一个值来动态确定。一旦分配了值,类型就固定了,所以如果给它指定另一个类型,将引起类型转换。
int256 x=12;
var y=x;//此时y的类型是int256
uint256 z=9;
y=z;//此时,报出异常,因为uint256不能转换为int256类型
但要注意的是:
在定义数组array和map时不能使用var。var也不能用于定义函数参数和状态变量
控制结构
if-else
while
for
breakcontinue
return
?:
等等
//结构上和其他语法没有什么差异
contract sample{
int a=12;
int[]b;
function sample(){
if(a==12){}
else if(a==34){}
else{}
var temp=10;
while(temp<20)
{
if(temp==17){break;}
else{continue;}
}
temp++;
}
for(var m=0;m<b.length;m++){
}
}
用new操作符创建合约
一个合约可以使用new关键字来创建一个新合约。
例如:
contract sample1{
int a;
function assign(int b){
a=b;
}
}
contract sample2{
function sample2(){
sample1 s=new sample1();//注意写法
s.assign(12);
}
}
异常
异常的抛出分为自动和手动。
若你想手动抛出异常,可以使用throw手动抛出。
注意,异常抛出后,会撤销对状态和余额的所有改变。
contract sample{
function myFunction(){
throw;
}
}
函数调用
内部函数调用:一个函数在同一个合约中调用另一个函数
外部函数调用:一个函数调用另一个合约的函数。