存档

2015年1月 的存档

微信开发(二)设置微信回调服务器 ( Node.js )

2015年1月31日 没有评论



概述

上一篇中简单介绍了Token的获取,这篇中介绍如何设置回调服务器。使用技术为Node.js中的Express。

搭建服务器

这里我使用Node.js中的Express框架实现一个简单的HTTP服务,主要目的是为了理解流程。真实的项目中我准备还是使用Java来做服务器,谁让我接手了一个Spring开发的项目呢 ⊙﹏⊙b 。

闲话少说,在自己的服务器上创建一个Express项目。(额 服务器当然是买的… )package.json 如下:

{
    "name":"weixinServer",
    "description":"wei xin server",
    "version":"0.0.1",
    "private":true,
    "dependencies": {
        "express":"4.x"
    }
}

安装后,创建一个文件app.js,输入以下代码:

var express = require('express');
var app = express();

function toWeb(req,res) {
    res.status(200).send("User Message");
}

function verifyServer(req,res) {
     var echostr = req.query.echostr;
     var sign = req.query.signature;
     var timestamp = req.query.timestamp;
     var nonce = req.query.nonce;
     console.log('recv weixin req:'," sign",sign,"timestamp",timestamp,"nonce",nonce,"echostr",echostr);
     res.status(200).send(""+echostr);
}

app.get('/test',function(req,res) {
    res.send("Hello Dear");
});

app.get('/weixin', function(req, res) {
    var echostr = req.query.echostr;
    if(echostr=='' || echostr == undefined || echostr==null) {
         toWeb(req,res);
    }
    else {
         verifyServer(req,res);
    }
});


var server = app.listen(80,function() {
    console.log('Listening on port %d',server.address().port);
});

因为我最终不用Node来搭建,所以就没校验,各位童鞋可以看看校验的文档 ,上面有拍黄片的示例代码,果然PHP是最好的语言微信开发(二)设置微信回调服务器 ( Node.js )

最后运行 sudo node app.js 完成服务器搭建。

设置回调

登陆微信公众平台,在开发者中心中选择 服务器配置 -> 修改配置 输入对应服务器的URL。点击确定即可完成校验。

分类: 未分类 标签:

微信开发(一)申请接口与token获取

2015年1月28日 没有评论



申请开发者

登陆微信公众平台 点选左侧的开发者中心,申请成为开发者。

成功后可以看到开发者中心界面,其中有AppId与AppSecret。目前AppSecret是部分隐藏的,如果要查看完整的版本需要绑定手机并刷二维码,搞的貌似很安全 微信开发(一)申请接口与token获取

获取Access_token

为了不暴露自动的AppId和AppSecret,我们要去向微信服务器要一个access_token 使用GET方法访问下面的网址:

https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET

参数说明

参数 是否必须 说明
grant_type 获取access_token填写client_credentia 
appid 第三方用户唯一凭证
secret 第三方用户唯一凭证密钥,即appsecret

访问后返回为Json:

{
    "access_token": "G4TT9jG3Y3S3pK4rBMxuCHFgRdo56SQKpC8WKv_4-rIeJ0UwnOaN9t8DCoIyKgSDAVAIKdV2cveWZ--oOEvsoYGy1jBTicXWQCgq2SHdo-Y",
    "expires_in": 7200
}

这个access_token就可以用于我们的服务器向微信服务器请求各种信息了。 相关文档在这里

访问接口

作为测试请求微信的服务器IP列表。

访问:

https://api.weixin.qq.com/cgi-bin/getcallbackip?access_token=ACCESS_TOKEN

把 ACCESS_TOKEN 替换为上一步请求到的文本。我们会看到返回了一个Json:

{
    "ip_list": [
        "101.226.62.77",
        "101.226.62.78",
        "101.226.62.79",
        "140.207.54.77",
        "140.207.54.78",
        "140.207.54.79",
        "140.207.54.80"
    ]
}

这些就是微信服务器的地址列表了。

错误列表

是网络请求就会出错,出错列表的官方文档在这里

分类: 未分类 标签:

『HTML5梦幻之旅』 – 舞动色彩,Canvas下实现颜色动画

2015年1月28日 没有评论

注:为了方便起见,本次开发用到了开源引擎lufylegend,官方地址如下:http://lufylegend.com/lufylegend

今天来学习下HTML5 Canvas颜色动画。什么是颜色动画呢?以我的理解就是以某种颜色过渡到另一种颜色。和这个效果有点类似:http://w3school.com.cn/tiy/t.asp?f=css3_animation1 上面的demo是用css3实现,而我们今天要用的是Canvas。Canvas并没有相关的API,所以要想实现这种效果,只有靠自己了。

从上面的例子可以看出,我们需要完成缓动动画和颜色变化这两个基本效果,再将这两个效果组合一下,就是我们要的结果了。

1,参考资料

值得高兴的是,lufylegend为我们提供了缓动类,可以完美地实现缓动动画。所以我们现在只需要实现颜色变化了。怎么变化颜色呢?纵观Canvas所有API,我发现像素操作也许能派上用场。恰巧,lufylegend在1.9.4和1.9.4以上的版本中提供了LColorTransform这个类,用于颜色值变幻。

以下是lufylegend API文档中LColorTransform用法:http://lufylegend.com/api/zh_CN/out/classes/LColorTransform.html

这个类可以配合LBitmapData的draw,colorTransform函数使用,这两个函数的用法可参考如下给出的地址。

draw:http://lufylegend.com/api/zh_CN/out/classes/LBitmapData.html#method_draw

colorTransform:http://lufylegend.com/api/zh_CN/out/classes/LBitmapData.html#method_colorTransform

另外,各位读者还需要了解LTweenLite:http://lufylegend.com/api/zh_CN/out/classes/LTweenLite.html

本次开发会使用draw函数和LTweenLite缓动类,请仔细阅读相关函数介绍,以便阅读下文时更轻松。

2,原理

实现颜色效果的原理其实很简单,就是通过缓动类作为驱动,不断改变颜色的RGB值。

在LColorTranform中,提供了redOffset,greenOffset,blueOffset这三个属性,分别用于像素处理时对R,G,B的调整。所以,在缓动类中,我们要缓动的属性就是这三个属性。然后在onUpdate中调用LBitmapData的draw函数对显示对象刷新。

3,代码展示

现在我先把代码展示一下:

<!DOCTYPE html>
<html>
<head>
	<title>Color Transform</title>
	<script type="text/javascript" src="./lufylegend-1.9.7.min.js"></script>
	<script type="text/javascript">
		LInit(50, "mylegend", 600, 400, main);

		function main () {
			var loader = new LLoader();
			loader.addEventListener(LEvent.COMPLETE, function (e) {
				rectColorTransform();
				imageColorTransform(e.target);
			});
			loader.load("./yorhomwang.png")
		}

		function rectColorTransform () {
			var w = 200, h = 240;

			var rectLayer = new LShape();
			rectLayer.graphics.drawRoundRect(0, "", [0, 0, w, h, 5], true, "#000000");

			var bmpd = new LBitmapData(null, 0, 0, w, h);
			var bmp = new LBitmap(bmpd);
			bmp.x = bmp.y = 20;
			addChild(bmp);

			var rect = new LRectangle(0, 0, w, h);
			var ct = new LColorTransform(1, 1, 1, 1, 255, 0, 0, 0);

			startTween(bmpd, rectLayer, ct, rect);
		}

		function imageColorTransform(content) {
			var bmpd = new LBitmapData(null, 0, 0, content.width, content.height);
			var bmp = new LBitmap(bmpd);
			bmp.x = 250;
			bmp.y = 20;
			addChild(bmp);

			var rect = new LRectangle(0, 0, bmpd.width, bmpd.height);
			var ct = new LColorTransform(1, 1, 1, 1, 0, 0, 0, 0);

			startTween(bmpd, new LBitmap(new LBitmapData(content, 0, 0, rect.width, rect.height)), ct, rect);
		}

		function startTween (bmpd, layer, ct, rect) {
			var update = function (o) {
				bmpd.draw(layer, null, o, null, rect);	
			};

			update(ct);

			LTweenLite.to(ct, 3, {
				blueOffset : 255,
				loop : true,
				onUpdate : update
			}).to(ct, 3, {
				redOffset : -255,
			}).to(ct, 3, {
				greenOffset : 255,
			}).to(ct, 3, {
				blueOffset : -255,
			}).to(ct, 3, {
				redOffset : 255,
			}).to(ct, 3, {
				greenOffset : -255,
			});
		}
	</script>
</head>
<body>
	<div id="mylegend"></div>
</body>
</html>

加上html代码,不足100行,可见,LColorTransform配合LTweenLite实现颜色动画还是很简单的。

运行上面的代码,得到如下效果:

『HTML5梦幻之旅』 - 舞动色彩,Canvas下实现颜色动画

『HTML5梦幻之旅』 - 舞动色彩,Canvas下实现颜色动画

测试地址:http://wyh.wjjsoft.com/demo/color_transform/

源代码已全部给出,大家直接复制粘贴吧~


由于用到了像素处理,所以在某些电脑上运行起来可能会很卡。但是在HTML5迅猛发展的时代里,相信Canvas渲染效率的大幅提升计日可待 。

经测试,在Chrome里运行效果会比其他浏览器好得多。

—————————————————————-

欢迎大家转载我的文章。

转载请注明:转自Yorhom’s Game Box

http://blog.csdn.net/yorhomwang

欢迎继续关注我的博客

分类: html5 标签:

【cocos2d-x 3.2 – JS -横版摇杆八方向移动】

2015年1月27日 没有评论

花了少许时间利用了一下cocos2dx 3.2Js版本+cocosstiduo2.0.6做了一个横版摇杆八方向的demo;

目前到今天为止,Js版本为3.2稳定版本,官网下载传送门:

http://www.cocos2d-x.org/filedown/cocos2d-js-v3.2.zip

 

Js环境搭载传送门:

【cocos2d-x 3.0 – JS -Mac配置篇】

 

轻松搭建完后,开始用JS写一个横版摇杆动作游戏的Demo,听起来貌似很高大上~~。

首先要做好几个准备:

1,角色精灵,我准备了一个骨骼动画精灵1个,cocosstiduo2.0.6制作的;

2,地图,也是用cocosstiduo2.0.6制作,生成出MainScene.csb 文件;

【cocos2d-x 3.2 - JS -横版摇杆八方向移动】

3,摇杆的PNG素材;

【cocos2d-x 3.2 - JS -横版摇杆八方向移动】 【cocos2d-x 3.2 - JS -横版摇杆八方向移动】

 

下面开始创建一个新的工程GoFighting,创建主场景MainLayer.js文件;

MainLayer继承BaseLayer.js,BaseLayer中处理这个层基本的页面处理,包括弹出的新层的遮罩处理;

 

BaseLayer.js:

【cocos2d-x 3.2 - JS -横版摇杆八方向移动】【cocos2d-x 3.2 - JS -横版摇杆八方向移动】

 1 var BaseLayer=cc.Layer.extend({
 2     _bgFrame:null,
 3     _notShowAnimation:null,
 4     _directorSteps:null,
 5     _showAnied:false,
 6     init:function(notShowAnimation){
 7         var bret=false;
 8         if(this._super()){
 9             //不可删除
10             var bgFrame = cc.LayerColor(cc.color(0,0,0,200));
11             this.addChild(bgFrame);
12             this._bgFrame=bgFrame;
13             this._notShowAnimation=notShowAnimation;
14             this.setAnchorPoint(cc.p(0.5,0.5));
15             this.ignoreAnchorPointForPosition(false);
16             if(!this._notShowAnimation){
17                 this.setScale(0.8);
18             }
19             this.setContentSize(winSize);
20             this.setPosition(cc.p(winSize.width/2,winSize.height/2));
21 
22             cc.eventManager.addListener({
23                 event: cc.EventListener.TOUCH_ONE_BY_ONE,
24                 swallowTouches: true,
25                 //onTouchMoved: this.onTouchMoved,
26                 onTouchBegan: function(){return true;}
27             }, this);
28 
29             bret=true;
30         }
31         return bret;
32     },
33     setBgColor:function(color){
34         this._bgFrame.setColor(color);
35     },
36 
37     onEnter:function(){
38         this._super();
39         if(!this._notShowAnimation&&!this._showAnied){
40             var sl=cc.EaseIn.create(cc.ScaleTo.create(0.15,1.1),2);
41             var sl2=cc.ScaleTo.create(0.15,1);
42             var seq=cc.Sequence.create(sl,sl2);
43             this.runAction(seq);
44             this._showAnied=true;
45         }
46     },
47 
48     onExit:function(){
49         this._super();
50         MemoryManager.getInstance().releaseMeoryFromOther();
51     }
52 });
53 
54 BaseLayer.OpenActionFs=function(obj){
55     obj.setScale(0.8);
56     if(obj!=null){
57         var sl=cc.EaseIn.create(cc.ScaleTo.create(0.15,1.1),2);
58         var sl2=cc.ScaleTo.create(0.15,1);
59         var seq=cc.Sequence.create(sl,sl2);
60         obj.runAction(seq);
61     }
62 };

View Code

 

首先我们加载主场景必须得背景图片,而主场景背景图片由cocosstiduo2.0.6制作,如何绑定呢?

在3.2引擎终目前还不支持直接使用widgetFromBinaryFile方法加载CSB,那么换成另外一种加载创建Node的方式:

ccs.csLoader.createNode(res.MainScene_CSB);

使用该方法需要自己去添加全路径

 /*
        cocostidio制作的CSB文件加载,注:
        在3.2引擎终目前还不支持直接使用widgetFromBinaryFile方法加载CSB
         */
        var baseroot = ccs.csLoader.createNode(res.MainScene_CSB);
        baseroot.setAnchorPoint(cc.p(0.5,0.5));
        baseroot.setPosition(this.getContentSize().width/2,this.getContentSize().height/2);
        this.baseroot=baseroot;
        this.addChild(baseroot,1,9001);
        BaseLayer.OpenActionFs(baseroot);

 

然后Run看一下效果:

【cocos2d-x 3.2 - JS -横版摇杆八方向移动】

 

然后继续加载角色的骨骼动画

        //角色骨骼动画加载
        var charname = "Char_014_1";
        var nowcharurl = resRole+charname+".ExportJson";
        if(jsb.fileUtils.isFileExist(nowcharurl)==true) {
            cc.log("nowcharurl =" + nowcharurl);
            ccs.ArmatureDataManager.getInstance().addArmatureFileInfo(nowcharurl);
            var hero = ccs.Armature.create(charname);
            this._hero_donghua = hero;
            hero.setPosition(cc.p(330, 260));
            hero.getAnimation().play("stand");
            hero.getAnimation().setMovementEventCallFunc(this.overStand, this);
           baseroot.addChild(hero, 3,99999);
        }

 

角色hero有回调,如跑动后停下来的回调:

    //移动完后回调
    overStand:function() {
        if(this._hero_donghua.getAnimation().getCurrentMovementID()=="")
        {
            this._hero_donghua.getAnimation().play("stand");
        }
    },

 

单单一个角色植入场景是否显得逻辑太单调,我们可以拖动这个精灵岂不更好,加一个简单的事件,让精灵活一点吧!!

//主角监听
var listener_Role = cc.EventListener.create({
    event: cc.EventListener.TOUCH_ONE_BY_ONE,
    swallowTouches: true,
    onTouchBegan: function (touch, event) {
        var target = event.getCurrentTarget();

        var locationInNode = target.convertToNodeSpace(touch.getLocation());
        var s = target.getContentSize();
        var rect = cc.rect(0, 0, s.width, s.height);

        if (cc.rectContainsPoint(rect, locationInNode)) {
            cc.log("sprite began... x = " + locationInNode.x + ", y = " + locationInNode.y);
            target.setOpacity(180);
            target.getAnimation().play("move");
            return true;
        }
        return false;
    },
    onTouchMoved: function (touch, event) {
        var target = event.getCurrentTarget();
        var delta = touch.getDelta();
        target.x += delta.x;
        target.y += delta.y;

    },
    onTouchEnded: function (touch, event) {
        var target = event.getCurrentTarget();
        cc.log("sprite onTouchesEnded.. ");
        target.setOpacity(255);
        target.getAnimation().play("stand");

    }
});

在ctor构造中添加角色事件的注册方法:

//人物
cc.eventManager.addListener(listener_Role, this._hero_donghua);

 

OK,我们再Run起来看看效果:

【cocos2d-x 3.2 - JS -横版摇杆八方向移动】

还可以拖动的呢!

 

然后,我们继续实现摇杆模式:

摇杆与事件有关,JS中摇杆须继承cc.EventListener去创建事件,事件类型cc.EventListener.TOUCH_ONE_BY_ONE(单点触摸方式);

看代码:

//摇杆监听
var listener_YaoGan = cc.EventListener.create({
    event: cc.EventListener.TOUCH_ONE_BY_ONE,
    swallowTouches: true,
    onTouchBegan: function (touch, event) {
        var target = event.getCurrentTarget();
        var locationInNode = target.convertToNodeSpace(touch.getLocation());
        //创建摇杆
        this.sprite_yaogan = new cc.Sprite(res.YaoGan_png);
        this.sprite_yaogan.attr({
            x: locationInNode.x,
            y: locationInNode.y
        });
        target.addChild(this.sprite_yaogan, 4,90099);

        //创建摇杆点
        this.sprite_yaogan_dian = new cc.Sprite(res.YaoGan_Dian_png);
        this.sprite_yaogan_dian.attr({
            x: locationInNode.x,
            y: locationInNode.y
        });
        target.addChild(this.sprite_yaogan_dian, 4,90999);

        return true;
    },

    onTouchMoved: function (touch, event) {
        //摇杆点
        var target = event.getCurrentTarget();
        var sp_dian = target.getChildByTag(90999);
        var sp_yaoganbd = target.getChildByTag(90099);
        var sp_hero = target.getChildByTag(99999);

        //摇起来
        if(sp_dian!=null&&sp_yaoganbd!=null)
        {
            var p_dian =  sp_yaoganbd.getPosition();
            var bd_width =sp_yaoganbd.getContentSize().width*0.5;
            cc.log("bd_width>>=="+bd_width);
            var point = touch.getLocation();
            var p_rad = this.getRad(p_dian,point);
            cc.log("p_rad>>=="+p_rad);
            //计算两个圆心之间距离
            var juli =Math.sqrt(Math.pow((p_dian.x - point.x),2) + Math.pow((p_dian.y - point.y),2));
            //距离不超过半径
            if(juli>=bd_width)
            {
                cc.log("go111>>>");
                sp_dian.setPosition(cc.pAdd(this.getAngelePosition(bd_width,p_rad),cc.p(p_dian.x,p_dian.y)));
            }
            else
            {
                cc.log("go2222>>>");
                var delta = touch.getDelta();
                sp_dian.x += delta.x;
                sp_dian.y += delta.y;
            }

//            //判断方向---四方向
//            if(p_rad>=-PI/4&&p_rad<PI/4)
//            {
//                R_Direction="right";
//            }
//            else if(p_rad>=PI/4&&p_rad<3*PI/4)
//            {
//                R_Direction="up";
//            }
//            else if((p_rad>=3*PI/4&&p_rad<=PI)||(p_rad>=-PI&&p_rad<-3*PI/4))
//            {
//                R_Direction="left";
//            }
//            else if(p_rad>=-3*PI/4&&p_rad<-PI/4)
//            {
//                R_Direction="down";
//            }


            //判断方向---八方向
            var move_x =  parseInt(p_dian.x -point.x);
            var move_y =  parseInt(p_dian.y -point.y);

            if(move_x>=10&&move_y<=-10)
            {
                //左上
                R_Direction = "left_up";
            }
            else if(move_x>=10&&move_y>=10)
            {
                //左下
                R_Direction = "left_down";
            }
            else if(move_x<=-10&&move_y<=-10)
            {
                //右上
                R_Direction = "rigth_up";
            }
            else if(move_x<=-10&&move_y>=10)
            {
                //右下
                R_Direction = "rigth_down";
            }
            else if(move_x>-10&&move_x<10&&move_y>0)
            {
                //
                R_Direction = "down";
            }
            else if(move_x>-10&&move_x<10&&move_y<0)
            {
                //
                R_Direction = "up";
            }
            else if(move_x>0&&move_y>-10&&move_y<10)
            {
                //
                R_Direction = "left";
            }
            else if(move_x<0&&move_y>-10&&move_y<10)
            {
                //
                R_Direction = "right";
            }

            R_Action="move";
            cc.log("R_Direction>>>"+R_Direction);
        }
    },

    //获取半径坐标
    getAngelePosition:function(r,angle){
        return cc.p(r*Math.cos(angle),r*Math.sin(angle));
    },

    //判断两点之间夹角
    getRad:function(pos1,pos2)
    {
        var px1 = pos1.x;
        var py1 = pos1.y;
        var px2 = pos2.x;
        var py2 = pos2.y;

        //得到两点x的距离
        var x = px2 - px1;
        //得到两点y的距离
        var y = py1 - py2;
        //算出斜边长度
        var xie = Math.sqrt(Math.pow(x,2) + Math.pow(y,2));
        //得到这个角度的余弦值(通过三角函数中的店里:角度余弦值=斜边/斜边)
        var cosAngle = x / xie;
        //通过反余弦定理获取到期角度的弧度
        var rad = Math.acos(cosAngle);
        //注意:当触屏的位置Y坐标<摇杆的Y坐标,我们要去反值-0~-180
        if (py2 < py1)
        {
            rad = -rad;
        }
        return rad;
    },

    onTouchEnded: function (touch, event) {
        var target = event.getCurrentTarget();
        if(target.getChildByTag(90099)!=null)
        {
            target.removeChildByTag(90099);
        }
        if(target.getChildByTag(90999)!=null)
        {
            target.removeChildByTag(90999);
        }

        R_Action="stand";

        var sp_hero = target.getChildByTag(99999);
        sp_hero.getAnimation().play("stand");
    }

});

在上面这个Js类中,包含了几个方法如,两点之间夹角的计算公式和最大半径坐标的计算公式;

因为我们需要在摇杆和摇杆点上面去做坐标处理,计算出夹角来对角色进行坐标位移操作,达到我们所需要的效果

跑起来的摇杆效果如下:

【cocos2d-x 3.2 - JS -横版摇杆八方向移动】

摇杆可以活动了,并且不能超过底下的背景半径,达到了我们需要的效果,下面就继续实现摇杆操控精灵移动的功能

可以继续在onTouchMoved: function (touch, event)事件终写方法获取一些判定参数:

//方向
var R_Direction = "";
//动作
var R_Action = "stand";
//移动速度
var R_speed = 4;

继续看listener_YaoGan类中的方向判断,我写了2种角色移动方法:

1,根据PI=3.1415 来计算 ,做了4方向的标识判断

2,根据坐标差值来计算,做了8方向的标识判断

OK,两种方法都可以行,可以自己拓展。

有了标识我们需要启动一个定时器来执行人物的操作

下面是定时器部分的代码:

//更新状态
    runGame:function(){

        if(R_Action=="move")
        {
            if(this._hero_donghua!=null)
            {
                if(this._hero_donghua.getAnimation().getCurrentMovementID()!="move")
                {
                    this._hero_donghua.getAnimation().play("move");
                }

                var p_hero_old = this._hero_donghua.getPosition();
                if(R_Direction=="right")
                {
                    this._hero_donghua.setScaleX(-1);
                    this._hero_donghua.setPosition(cc.p(p_hero_old.x+R_speed,p_hero_old.y));

                }
                else if(R_Direction=="up")
                {
                    this._hero_donghua.setPosition(cc.p(p_hero_old.x,p_hero_old.y+R_speed));

                }
                else if(R_Direction=="left")
                {
                    this._hero_donghua.setScaleX(1);
                    this._hero_donghua.setPosition(cc.p(p_hero_old.x-R_speed,p_hero_old.y));

                }
                else if(R_Direction=="down")
                {
                    this._hero_donghua.setPosition(cc.p(p_hero_old.x,p_hero_old.y-R_speed));

                }
                else if(R_Direction=="left_up")
                {
                    this._hero_donghua.setScaleX(1);
                    this._hero_donghua.setPosition(cc.p(p_hero_old.x-R_speed,p_hero_old.y+R_speed));

                }
                else if(R_Direction=="left_down")
                {
                    this._hero_donghua.setScaleX(1);
                    this._hero_donghua.setPosition(cc.p(p_hero_old.x-R_speed,p_hero_old.y-R_speed));

                }
                else if(R_Direction=="rigth_up")
                {
                    this._hero_donghua.setScaleX(-1);
                    this._hero_donghua.setPosition(cc.p(p_hero_old.x+R_speed,p_hero_old.y+R_speed));
                }
                else if(R_Direction=="rigth_down")
                {
                    this._hero_donghua.setScaleX(-1);
                    this._hero_donghua.setPosition(cc.p(p_hero_old.x+R_speed,p_hero_old.y-R_speed));

                }
            }
        }
    }

OK,人物可以根据摇杆八方向的跑动起来了,我们Run起来看看效果,应该很赞!

【cocos2d-x 3.2 - JS -横版摇杆八方向移动】

 

嗯,该Demo就开发完毕了,下面是整个DEMO的下载地址,希望能大家对大家起到帮助;

cocos2d-x 3.2 – JS -横版摇杆八方向移动DEMO下载地址

自己创建一个新的工程,将ZIP解压文件拷贝到工程根目录就可以Run起来,替换main.js和project.json;

 

分类: 未分类 标签:

vim 安装Emmet插件

2015年1月26日 没有评论

Why html

最近说好的要学做移动App,想来想去,还是先从基础搞起,先把失散多年的html捡捡。虽然我会原生语言,不过用惯了跨平台的东西:

vim 安装Emmet插件

 

想来想去,既然是应用,效率应该还成,html应该够用了。跨得一手好平台。作为vim党,我找到了个插件,看着还不错。

what’s Emmet

Emmet是vi的一个插件,它可以让我等更方便的编辑html。你能在 这里 找到它的最新信息。

安装

下载项目解压缩。找到文件中的pluginautoload将它复制到

~/.vim/.

重启终端就成了。

测试

创建一个文件,用vi打开,输入:

html:5

注意要将光标至于最后的位置,我觉得它的原理是,检测从行首到光标位置的表达式,因此如果你光标在其他位置,表达式就不完整了。

接下来就是见证奇迹的时刻,在输入模式下,按下ctrl+y然后按下”,”。就会自动生成如下代码:

<!DOCTYPE HTML>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    _
</body>
</html>

看起来好酸爽。

More

当然它还有更多的用法。自动生成格式文本、图片啥的,你可以参考 官方的文档 英文比较简单,我就不人肉翻译了 ; )

 

分类: 基础知识 标签:

Box2D物理引擎模拟炸弹爆炸效果

2015年1月25日 没有评论

今天咱们来模拟炸弹效果。于是问题一来了:“为什么要模仿这么暴力的效果呢?莫非几日不见,Yorhom人品煞变?”

其实玩过愤怒的小鸟的同学都应该对这种效果似曾相识,因为据非官方报道,第二厉害的小鸟——黑色鸟的特技就是自爆。问题二出现了:“那第一厉害的小鸟是哪一种呢?”据Yorhom我本人测试,那只红色大鸟应该是最厉害的,不过貌似没有特技?愤怒的小鸟这种肤浅的游戏,Y某我最擅长了,以后有时间会专门写写这个游戏的攻略。这两种鸟的靓照如下:

Box2D物理引擎模拟炸弹爆炸效果Box2D物理引擎模拟炸弹爆炸效果

敷衍了问问题二的同学,问题三就来了:“到底如何实现这个效果呢?” 这个问题不好敷衍,于是接下来的文章,就由我向大家介绍如何使用Box2D实现这种效果。

在学习本章前,你需要先了解一下Box2D这款很实用,很出名的物理引擎。凭借我个人的学习经验,我向大家推荐拉登大叔的博客,其博客Box2D栏目地址如下:

http://www.ladeng6666.com/blog/category/box2d/

一,炸弹效果原理

在实现这个效果的时候,我想到了两种方案。

方案一:在炸弹刚体爆炸时,由炸弹刚体为起点,向四周喷散小刚体,这些小刚体会被喷射到附近的刚体上,对附近的刚体施加力,然后力是物体运动状态改变的原因(摘自高中物理必修一),然后爆炸效果就可以完成了。这个方法比较简单,但是我觉得有点dirty way。像我这种耳机标有R的必须带右边,标有L的必须带左边的人,怎么可能就此满足了呢?当然,感兴趣的朋友可以自己尝试一下这种方法~

方案二:首先想办法把炸弹刚体周围的其他刚体找到,然后对它们施加一个力,我们只用控制好力的方向就可以实现爆炸效果了。嗯~不错。似乎这种方法更漂亮~

这两种方案显而易见的区别就是方案二比方案一字数要少一点。这意味着什么呢?我想,大家都应该做过至少6年的数学题了吧,据我大约9.5年来总结的经验,题目字数越少的数学题就越难,因为提供的信息量少。所以说,虽然方案二效果相对更好,但是实现起来可能要复杂很多。

方案二之所以比较难以实现,是在于我们不知道如何查找周围的刚体。为此,我将我所了解到的Box2D中的功能都涉猎了一遍,发现我从来没有用过只是听说过的一个功能——射线投射可能会派上用场。不过辛好网上相关的资料挺多的,所以我就此学习了一下b2RayCastInput,b2RayCastOutput这两个类。

上述的两个类很重要,所以大家必须先了解这两个类,具体的教程为:http://ohcoder.com/blog/2012/12/10/ray-casting/,教程中用的是Box2D C++,不过无论是C++版还是Web版,用法都是一样的。

b2RayCastInput,b2RayCastOutput这两个类到底有什么用的?从上面提供的教程可以看出,这两个类的实例一般作为参数传入b2Fixture的RayCast函数中,这个函数用来检查某个射线是否于刚体相交。所以我们只用以炸弹刚体位置为始点,然后向周围喷射射线。然后循环世界里的刚体,判断循环得到的刚体是否与射线相交,如果相交则对当前刚体施加一个力。这样一来,炸弹效果的原理基本就被弄清除了。

二,构建一个物理世界

先把最基础的世界创建出来把。于是,我们在index.html里加入以下代码:

<!DOCTYPE html>
<html>
<head>
	<title>Box2d Bomb</title>
	<script type="text/javascript" src="./Box2dWeb-2.1.a.3.min.js"></script>
	<script type="text/javascript" src="./Main.js"></script>
</head>
<body>
	<canvas id="mycanvas" width="600" height="400"></canvas>	
</body>
</html>

html代码不是重点,重点是Main.js:

window.addEventListener("load", main, false);

var b2Vec2 = Box2D.Common.Math.b2Vec2
, b2AABB = Box2D.Collision.b2AABB
, b2BodyDef = Box2D.Dynamics.b2BodyDef
, b2Body = Box2D.Dynamics.b2Body
, b2FixtureDef = Box2D.Dynamics.b2FixtureDef
, b2Fixture = Box2D.Dynamics.b2Fixture
, b2World = Box2D.Dynamics.b2World
, b2MassData = Box2D.Collision.Shapes.b2MassData
, b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape
, b2CircleShape = Box2D.Collision.Shapes.b2CircleShape
, b2DebugDraw = Box2D.Dynamics.b2DebugDraw
, b2MouseJointDef =  Box2D.Dynamics.Joints.b2MouseJointDef
, b2RayCastInput = Box2D.Collision.b2RayCastInput
, b2RayCastOutput = Box2D.Collision.b2RayCastOutput;

var world, fixDef, bodyDef;

var bomb = null;

var timer = 0;

function main () {
	world = new b2World(new b2Vec2(0, 9.8), true);

	fixDef = new b2FixtureDef();
	fixDef.density = 1.0;
	fixDef.friction = 0.5;
	fixDef.restitution = 0.2;

	bodyDef = new b2BodyDef();

	createGround();
	createBlocks();
	createBomb();
	createDebugDraw();
	setInterval(update, 1000 / 60);
}

function createGround () {
	fixDef.shape = new b2PolygonShape();
	fixDef.shape.SetAsBox(20, 1);
	
	bodyDef.type = b2Body.b2_staticBody;
	bodyDef.position.Set(10, 13);

	world.CreateBody(bodyDef).CreateFixture(fixDef);
}

function createBlocks () {
	var list = [
		{width : 120, height : 30, x : 140, y : 330},
		{width : 20, height : 100, x : 50, y : 200},
		{width : 20, height : 100, x : 230, y : 200},
		{width : 120, height : 20, x : 140, y : 80},
		{width : 120, height : 30, x : 440, y : 330},
		{width : 20, height : 100, x : 350, y : 200},
		{width : 20, height : 100, x : 530, y : 200},
		{width : 120, height : 20, x : 440, y : 80}
	];

	for (var i = 0; i < list.length; i++) {
		var data = list[i];

		fixDef.shape = new b2PolygonShape();
		fixDef.shape.SetAsBox(data.width / 30, data.height / 30);
		
		bodyDef.type = b2Body.b2_dynamicBody;
		bodyDef.position.Set(data.x / 30, data.y / 30);

		world.CreateBody(bodyDef).CreateFixture(fixDef);
	}
}

function createBomb () {
	fixDef.shape = new b2CircleShape(0.5);

	bodyDef.type = b2Body.b2_dynamicBody;
	bodyDef.position.Set(9.7, 1);

	bomb = world.CreateBody(bodyDef);
	bomb.userData = "iambomb";
	bomb.CreateFixture(fixDef);
}

function createDebugDraw () {
	var debugDraw = new b2DebugDraw();
	debugDraw.SetSprite(document.getElementById("mycanvas").getContext("2d"));
	debugDraw.SetDrawScale(30.0);
	debugDraw.SetFillAlpha(0.5);
	debugDraw.SetLineThickness(1.0);
	debugDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit | b2DebugDraw.e_controllerBit | b2DebugDraw.e_pairBit);
	world.SetDebugDraw(debugDraw);
}

function update () {
	world.Step(1 / 60, 10, 10);
	world.DrawDebugData();
	world.ClearForces();

	if (timer++ == 100) {
		detonated();
	}
}

值得注意的是timer这个变量,这个变量是用来为爆炸计时的。在update函数里我们调用了dentonated这个函数,我暂时不把这个函数的代码公布,这个函数是引爆炸弹用的。
运行代码,我们得到如图的物理世界:

Box2D物理引擎模拟炸弹爆炸效果

图中的小球实际上就是一颗炸弹。

三,引爆炸弹

上面提到过dentonated这个函数,这里,我们就来学习一下里面的代码,这也是本次开发的核心部分。

function detonated () {
	var position = bomb.GetWorldCenter();

	var range = 3;

	for (var i = 0; i <= 100; i++) {
		var angle = 360 / 100 * i;

		var input = new b2RayCastInput();
		input.p1 = position;
		input.p2.Set(position.x + range * Math.cos(angle), position.y + range * Math.sin(angle));
		input.maxFraction = 1;

		var output = new b2RayCastOutput();

		for (var currentBody = world.GetBodyList(); currentBody; currentBody = currentBody.GetNext()) {
			if (currentBody.userData == bomb.userData) {
				continue;
			}

			var fix = currentBody.GetFixtureList();

			if (!fix) {
				continue;
			}
			
			var isHit = fix.RayCast(output, input);

			if (isHit) {
				var p1 = input.p1.Copy();
				var p2 = input.p2.Copy();
				p2.Subtract(p1);
				p2.Multiply(output.fraction);
				
				p1.Add(p2);

				var hitPoint = p1.Copy();
				
				hitPoint.Subtract(position);

				currentBody.ApplyForce(new b2Vec2(hitPoint.x * (1 - output.fraction) * 300, hitPoint.y * (1 - output.fraction) * 300), hitPoint);
			}

		}
	}

	world.DestroyBody(bomb);
}

在这个函数里,我首先用GetWorldCenter获取到炸弹刚体的位置,然后设定一个爆炸范围变量range。然后通过循环开始向四周创建射线。这里我设定一共要喷出100条射线,所以,每条射线间的夹角为3.6度。知道角度后,我们就可以计算每条射线向量的终点了,并将终点和始点传入b2RayCastInput中。

一系列射线方面的准备完毕后,就要开始循环世界里的刚体了。然后将循环得到的刚体获取刚形(一个b2Fixture对象),并调用刚型的RayCast来判断刚才创建的射线是否与刚体相交。

if (isHit) {
	var p1 = input.p1.Copy();
	var p2 = input.p2.Copy();
	p2.Subtract(p1);
	p2.Multiply(output.fraction);
	
	p1.Add(p2);

	var hitPoint = p1.Copy();
	
	hitPoint.Subtract(position);

	currentBody.ApplyForce(new b2Vec2(hitPoint.x * (1 - output.fraction) * 300, hitPoint.y * (1 - output.fraction) * 300), hitPoint);
}

以上几行代码负责计算射线与刚体相交的位置所对应的向量,并在该位置上施加一个力。那么如何计算射线与刚体相交的位置所对应的向量呢?我们不妨来看张图:Box2D物理引擎模拟炸弹爆炸效果

现在,让我们调动起高中数学必修四里平面向量的知识。于是数学问题来了:已知向量Op1Op2坐标,且p1,p2,p3三点共线,p1p3 = n p1p2,求Op3

要解这道题,可以用定比分点等知识,用向量加减,数乘运算也能解决问题。以下是我的解答:

解 设p1(x1, y1),p2(x2, y2)

Op3 = Op1 + p1p3 = np1p2 + Op1 = n(x2 – x1, y2 – y1) + (x1, y1) = (nx2 – nx1 + x1, ny2 – ny1 + y1)

这是数学写法,换到程序代码里就是:

var p1 = input.p1.Copy();
var p2 = input.p2.Copy();
p2.Subtract(p1);
p2.Multiply(output.fraction);

p1.Add(p2);

var hitPoint = p1.Copy();

这里的output.fraction就相当于上题所给的n。Copy,Substact,Add,Multiply分别是用来复制当前向量,向量减法,向量加法,向量数乘运算。

计算完了碰撞位置,我们给刚体用ApplyForce施加一个力,然后刚体就类似于被一股爆炸产生的气流冲击了似的,飞了出去~

炸弹爆炸了炸弹本身肯定不负存在了,所以在给周围的刚体施加完力后,用DestroyBody销毁掉这个炸弹刚体~

运行代码,得到下图:

Box2D物理引擎模拟炸弹爆炸效果

在线测试地址:http://wyh.wjjsoft.com/demo/box2d_bomb/

源代码下载地址:http://wyh.wjjsoft.com/downloads/box2d_bomb.zip

本章就到此结束了,欢迎大家留言~

—————————————————————-

欢迎大家转载我的文章。

转载请注明:转自Yorhom’s Game Box

http://blog.csdn.net/yorhomwang

欢迎继续关注我的博客

分类: box2D 标签:

Converting Unityscript to c#

2015年1月25日 没有评论

Can anyone help me convert this Unityscript code to C# in Unity3d. I am having trouble understanding variable text and transform (i.e var text: Transform;)

#pragma strict


var text: Transform;

Based on the code you’ve posted thus far, the C# equivalent would be:

Transform text;

Transform is the type. text is the name of the variable being declared.

Unity actually provides a beginner video that explains the syntax differences between JS and C#:

Unity – C# vs JS syntax

分类: stackoverflow精选, unity3d 标签:

Unity3D研究:Unity3D引擎架构设计

2015年1月22日 没有评论

组件(Component)这个概念最早是在2005年《Game Programming Gems 5》的《Component Based Object Management》中接触到的,当时感觉在设计上很实用。后来,发现Unreal Engine 3的一个重要的改进就是抛弃了以前的基于纯派生关系的对象
模型 ,而转为使用
基于组件
的对象 模型 。对于这种设计思想,Unity比Unreal贯彻的更彻底——一切皆Component。

那么到底什么是“基于组件”的对象 模型 ?它能够解决什么问题?

在传统的设计中,我们一般会使用“派生”来描述对象之间的关系。子类通过派生父类,来获得父类的功能。在设计游戏对象时,会根据游戏本身的需要而为游戏对象添加各种功能支持,比如渲染,碰撞,刚体,粒子系统等等。这些通用功能为了能够为各种派生类提供服务,都必须实现到基类中。这样就导致了游戏对象基类变得非常庞大臃肿,即难使用,又难维护。

”基于组件“的对象 模型 就是把所有需要提供给游戏对象的基础功能都独立成单独的”组件模块“(Component),一个具体的游戏对象可以将它需要的功能模块组合到一起使用。所有”功能“不再是父类中的接口,而变成子对象实例,为游戏对象提供服务。这样既保证了功能代码的可重用性,又增加了整个对象体系的模块化和灵活度。

在Unity中,GameObject除了作为Component的容器之外,基本上没有其他功能。所有需要的功能都要通过组合Component来实现。脚本本身也是Component,用来在GameObject上通过控制其他Component来实现自定义的功能。虽然这些Component在物理上是完全并列的关系,但是他们之间还是会有一定的层次关系的。在设计一个游戏对象的具体功能时,组件一般会被分为三个层次。

引擎的基础组件

Unity本身提供的各种内部功能组件。比如渲染组件,物理组件,声音组件等等。这些组件实现了所有引擎提供的基础功能,会被脚本使用来组合高级功能。

模块功能脚本组件

通过脚本实现的一些相对独立的通用模块功能的组件。这类 组件的设计 是脚本可重用的关键,需要仔细分析游戏对象中哪些功能可以被独立出来成为一个可重用的功能模块组件,并且在实现上应该尽量降低与其他组件的耦合性。比如在设计一个角色游戏对象时,需要为他设计换装功能。换装功能其实就是对显示子对象进行分组管理,切换显示状态。这个功能相对独立,与其将他实现到角色中,不如独立成一个功能模块组件。角色游戏对象和其他所有需要换装功能的游戏对象都可以通过包含这个模块组件来实现换装功能。

模块功能组件之间还可能有依赖关系,也就是一个功能模块组件可能依赖与另一个功能模块组件,从而在这个组件层次上形成更多的子层次。

高层的胶水代码脚本

这些脚本用来真正将引擎基础组件和模块功能组件组合到一起实现最终游戏对象逻辑。用“胶水代码”来形容这些脚本非常的贴切,就是把所有这些子功能“粘”在一起。比如设计一个Player脚本,将所有需要的组件功能组合起来,实现一个玩家的具体游戏逻辑。因为这一层次代表的都是最高层的游戏行为控制对象,是具体的游戏逻辑的“胶水”代码,不会再为更上层提服务,所以本身的可重用性并不高。但是这些对象之间按照类型区分,往往会有一些功能上的重合,所以反而可以继续使用派生关系来实现功能的重用。比如在Character中实现所有的基础功能(这些功能又是通过组合基础组件来实现的),而Player和NPC都从Character派生,来继承所有Character的功能,并继续实现自己特殊的功能。一个功能到底应该用组件实现还是用派生实现并没有非常明确的界限,应该根据需要灵活运用。

在使用Unity的过程中,如果要实现的是demo级别的小工程,并不需要考虑很多,直接用脚本实现功能就可以了。但是如果要有效地组织复杂的工程,提高代码的重用性,充分理解和合理的利用“基于组件”的对象
模型 设计思想还是很重要的。

分类: unity3d 标签:

Unity3D研究:如何更合理的架构你的游戏脚本

2015年1月22日 没有评论

这篇文章主要想大家说明一下我在Unity3D游戏开发中是如何写游戏脚本的,对于Unity3D这套游戏引擎来说入门极快,可是要想做好却非常的难。这篇文章的目的是让哪些已经上手Unity3D游戏引擎的朋友学会如何更好的写游戏脚本,当然本文这紧紧是我这么多年对游戏开发的认知,你也可以有你自己的看法。首先我们看看游戏主要是由哪几部分组成的,如下图所示,任何平台下的任何游戏核心都是由:数据、逻辑、渲染三大部分组成。

当你写过》=2个平台下的游戏时你会发现其实游戏开发很“容易”,为什么“容易”呢?因为此时你会发现所有平台下开发游戏的模式,如下图中的“数据”与“逻辑”两部分真的是完全一样的,这两部分是与游戏开发平台无关的。然而真正与游戏平台有关的紧紧是“渲染”这部分,因为各个游戏平台下的渲染接口是不同的。这也就印证了一点,能把J2ME游戏写好的程序员就必然能把IOS或Android游戏同样的写好。读到这里请结合一下你的公司情况,你可能会发现在你的技术总监两三天就能上手Unity3D游戏开发 Cocos2d游戏开发,这并不是他对游戏平台研究的透彻,而是他对游戏数据的掌控能力非常强,所以能很快玩转各个平台下的开发。

         如下图所示,Unity3D这套游戏引擎在游戏开发中的权重如图中所示。其中包含100%的渲染部分 +50%左右的逻辑部分。(因为Unity3D封装了很多与逻辑相关的API供开发者使用)

 

        下面我们回到Unity3D脚本架构的编写上,我们知道Unity3D在是可以创建游戏场景的,在每个游戏场景中又可以创建游戏对象,把每个场景的游戏对象融合在一起就是一款3D游戏。游戏场景之间属于同等级的关系,为了让游戏场景之前交互我们需要有一个凌驾所有场景之上的脚本,我称之为“全局脚本”。如下图所示,所有场景都能与这个唯一的全局脚本进行交互。举个例子,当场景切换时可将临时逻辑数据写入全局脚本中,切换完毕后再去全局脚本中取之前保存的数据,从而实现交互。(当然还有别的办法也能实现这个效果,但是我觉得这样做会更好一些,数据会更安全一些)

         接着我们就进入场景中,游戏场景是由若干游戏对象组成,下面我好好说一说游戏对象。游戏对象是需要绑定游戏脚本才能完成它的生命周期。那么脚本的使命就会尤其的重要。因为游戏对象比较多那么脚本必然会出现交互的情况,如下图所示,很多初期Unity3D的项目中的脚本会编写成这个样子。错综复杂相互交互,这样编写的脚本有可能你的游戏能做出来,可是你在维护的时候团队开发的时候你会发现你的脚本非常的混乱,别的同事想改都不知道怎么改。(显然这样的作法时完全错误的)

         我们想想为什么脚本之间要交互,原因很简单。是因为脚本中需要使用/调用另一条脚本或者另一条脚本对应的游戏对象某一项数据/方法,为了解决这个问题而导致最终的脚本非常混乱。为了避免这个问题,我在开发中会这么做,如下图所示,脚本之间切记不要做直接的相互交互,脚本之间只做间接的交互。每一个游戏场景都有一个凌驾所有游戏对象之上的单例脚本,在这条脚本中保存场景中所有脚本的公共数据。包括该场景的整体逻辑更新都是在这条单例脚本中完成。每条脚本都只与这个单例脚本做交互,和别的脚本一概不交互。(间接交互)

         编写脚本时请注意,脚本只干属于自己最重要的事情,就跟代码中的函数一样,只干最重要的事情。切记和该条脚本无关的事情不要去管,不要在脚本中做过多的相互连带工作,让所有连带工作的话都放在全局单例脚本中来做。

  这里我们举一个例子,主角砍怪或技能攻击怪,怪物受伤只到怪死亡以后屏幕播放一段胜利动画。

1.主角对象发动攻击,全局单例脚本接受按键事件后通知主角脚本播放攻击动画。

2.敌人对象接受到主角发送攻击消息时开始播放受伤动画,敌人脚本接收到主角的碰撞时询问单例脚本 主角是“普通攻击、还是技能攻击”,接着敌人播放对应的受伤动画,根据攻击类型敌人对象开始减血。

3.重复上面的操作,当敌人的血量《=0的时。敌人销毁自身对象,并且敌人脚本告诉单例脚本自己已经死亡。此时,单例脚本在调用“胜利动画”对象播放胜利动画效果。

上述逻辑我是完全按照刚刚图片中所说明的方式来写,这样做就可以很好的避免交互交互混乱的情况,其实开发中的所有类似这种交互的情况都能很好的用这个全局单例脚本来解决。希望广大Unity3D开发爱好者可以和我讨论,因为我知道架构设计没有最好只有更好。嚯嚯!!

这篇文章主要想大家说明一下我在Unity3D游戏开发中是如何写游戏脚本的,对于Unity3D这套游戏引擎来说入门极快,可是要想做好却非常的难。这篇文章的目的是让哪些已经上手Unity3D游戏引擎的朋友学会如何更好的写游戏脚本,当然本文这紧紧是我这么多年对游戏开发的认知,你也可以有你自己的看法。首先我们看看游戏主要是由哪几部分组成的,如下图所示,任何平台下的任何游戏核心都是由:数据、逻辑、渲染三大部分组成。

当你写过》=2个平台下的游戏时你会发现其实游戏开发很“容易”,为什么“容易”呢?因为此时你会发现所有平台下开发游戏的模式,如下图中的“数据”与“逻辑”两部分真的是完全一样的,这两部分是与游戏开发平台无关的。然而真正与游戏平台有关的紧紧是“渲染”这部分,因为各个游戏平台下的渲染接口是不同的。这也就印证了一点,能把J2ME游戏写好的程序员就必然能把IOS或Android游戏同样的写好。读到这里请结合一下你的公司情况,你可能会发现在你的技术总监两三天就能上手Unity3D游戏开发 Cocos2d游戏开发,这并不是他对游戏平台研究的透彻,而是他对游戏数据的掌控能力非常强,所以能很快玩转各个平台下的开发。

 

         如下图所示,Unity3D这套游戏引擎在游戏开发中的权重如图中所示。其中包含100%的渲染部分 +50%左右的逻辑部分。(因为Unity3D封装了很多与逻辑相关的API供开发者使用)

 

 

        下面我们回到Unity3D脚本架构的编写上,我们知道Unity3D在是可以创建游戏场景的,在每个游戏场景中又可以创建游戏对象,把每个场景的游戏对象融合在一起就是一款3D游戏。游戏场景之间属于同等级的关系,为了让游戏场景之前交互我们需要有一个凌驾所有场景之上的脚本,我称之为“全局脚本”。如下图所示,所有场景都能与这个唯一的全局脚本进行交互。举个例子,当场景切换时可将临时逻辑数据写入全局脚本中,切换完毕后再去全局脚本中取之前保存的数据,从而实现交互。(当然还有别的办法也能实现这个效果,但是我觉得这样做会更好一些,数据会更安全一些)

 

 

         接着我们就进入场景中,游戏场景是由若干游戏对象组成,下面我好好说一说游戏对象。游戏对象是需要绑定游戏脚本才能完成它的生命周期。那么脚本的使命就会尤其的重要。因为游戏对象比较多那么脚本必然会出现交互的情况,如下图所示,很多初期Unity3D的项目中的脚本会编写成这个样子。错综复杂相互交互,这样编写的脚本有可能你的游戏能做出来,可是你在维护的时候团队开发的时候你会发现你的脚本非常的混乱,别的同事想改都不知道怎么改。(显然这样的作法时完全错误的)

 

 

         我们想想为什么脚本之间要交互,原因很简单。是因为脚本中需要使用/调用另一条脚本或者另一条脚本对应的游戏对象某一项数据/方法,为了解决这个问题而导致最终的脚本非常混乱。为了避免这个问题,我在开发中会这么做,如下图所示,脚本之间切记不要做直接的相互交互,脚本之间只做间接的交互。每一个游戏场景都有一个凌驾所有游戏对象之上的单例脚本,在这条脚本中保存场景中所有脚本的公共数据。包括该场景的整体逻辑更新都是在这条单例脚本中完成。每条脚本都只与这个单例脚本做交互,和别的脚本一概不交互。(间接交互)

 

 

         编写脚本时请注意,脚本只干属于自己最重要的事情,就跟代码中的函数一样,只干最重要的事情。切记和该条脚本无关的事情不要去管,不要在脚本中做过多的相互连带工作,让所有连带工作的话都放在全局单例脚本中来做。

  这里我们举一个例子,主角砍怪或技能攻击怪,怪物受伤只到怪死亡以后屏幕播放一段胜利动画。

1.主角对象发动攻击,全局单例脚本接受按键事件后通知主角脚本播放攻击动画。

2.敌人对象接受到主角发送攻击消息时开始播放受伤动画,敌人脚本接收到主角的碰撞时询问单例脚本 主角是“普通攻击、还是技能攻击”,接着敌人播放对应的受伤动画,根据攻击类型敌人对象开始减血。

3.重复上面的操作,当敌人的血量《=0的时。敌人销毁自身对象,并且敌人脚本告诉单例脚本自己已经死亡。此时,单例脚本在调用“胜利动画”对象播放胜利动画效果。

上述逻辑我是完全按照刚刚图片中所说明的方式来写,这样做就可以很好的避免交互交互混乱的情况,其实开发中的所有类似这种交互的情况都能很好的用这个全局单例脚本来解决。希望广大Unity3D开发爱好者可以和我讨论,因为我知道架构设计没有最好只有更好。嚯嚯!!

分类: unity3d 标签:

uGUI使用代码动态添加Button.OnClick()事件(Unity3D开发之十二)

2015年1月14日 没有评论

猴子原创,欢迎转载。转载请注明: 转载自Cocos2Der-CSDN,谢谢!

原文地址: http://blog.csdn.net/cocos2der/article/details/42705885

uGUI出来这么久了,也一直没好好用用,主要是公司项目不用U3D。昨晚用了下Button,还是比较爽的。

主要说下用代码添加button.OnClick()事件的方法(使用属性面板添加的方法就不说了)

一、创建2D UI Panel,添加你需要的Button。

我添加了3个Button:BtnStart,BtnShop,BtnLeaderboards。

uGUI使用代码动态添加Button.OnClick()事件(Unity3D开发之十二)

二、添加脚本

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.Events;
using UnityEngine.UI;


public class MainMenu : MonoBehaviour {

	// Use this for initialization
	void Start () {
		List<string> btnsName = new List<string>();
		btnsName.Add("BtnPlay");
		btnsName.Add("BtnShop");
		btnsName.Add("BtnLeaderboards");

		foreach(string btnName in btnsName)
		{
			GameObject btnObj = GameObject.Find(btnName);
			Button btn = btnObj.GetComponent<Button>();
			btn.onClick.AddListener(delegate() {
				this.OnClick(btnObj); 
			});
		} 
	}

	public void OnClick(GameObject sender)
	{
		switch (sender.name)
		{
		case "BtnPlay":
			Debug.Log("BtnPlay");
			break;
		case "BtnShop":
			Debug.Log("BtnShop");
			break;
		case "BtnLeaderboards":
			Debug.Log("BtnLeaderboards");
			break;
		default:
			Debug.Log("none");
			break;
		}
	}
	
	// Update is called once per frame
	void Update () {
	
	}
}

ok,测试一下,可以玩耍了。

================== 2015-03-07 更新 ======================

有人问道如果是Toogle如果添加呢?其实很简单的。

// Add click listenner for buttons
		Hashtable btnsName = new Hashtable();
		btnsName.Add("BtnNewGame", "Button");
		btnsName.Add("BtnContinue", "Button");
		btnsName.Add("BtnChallenge", "Button");
		btnsName.Add("BtnMoreGame", "Button");
		btnsName.Add("BtnRank", "Button");
		btnsName.Add("BtnMusic", "Toggle");
		btnsName.Add("BtnSound", "Toggle");
		foreach(DictionaryEntry btnInfo in btnsName) 
		{  
			GameObject btnObj = GameObject.Find(btnInfo.Key as string);  
			if (btnInfo.Value == "Button") {
				Button btn = btnObj.GetComponent<Button>();  
				btn.onClick.AddListener(delegate() {  
					// this.OnClick(btnObj);   
				}); 
			}
			else if (btnInfo.Value == "Toggle") {
				Toggle btn = btnObj.GetComponent<Toggle>();  
				btn.onValueChanged.AddListener(delegate(bool isOn) {
					// this.OnValueChanged(isOn, btnObj);
				});
			}
		}

分类: 未分类 标签: