数据专栏

智能大数据搬运工,你想要的我们都有

科技资讯:

科技学院:

科技百科:

科技书籍:

网站大全:

软件大全:

轻量级监控平台之cpu监控脚本 #!/bin/bash #进程监控脚本 #功能需求: 上报机器的硬件层面-cpu负载数据 . /etc/profile . ~/.bash_profile pushurl="https://lightmonitorapi.test.cn/push/cpuload" #抓取cpu的总核数 cpu_num=`grep -c 'model name' /proc/cpuinfo` #抓取当前系统15分钟的平均负载值 load_15=`uptime | awk '{print $NF}'` #计算当前系统单个核心15分钟的平均负载值,结果小于1.0时前面个位数补0。 average_load=`echo "scale=2;a=$load_15/$cpu_num;if(length(a)==scale(a)) print 0;print a" | bc` cpuload=$( uptime | awk '{print $10 $11 $NF}') msg=$cpuload","$average_load ip=$(ip addr | grep 'state UP' -A2 | tail -n1 | awk '{print $2}' | cut -f1 -d '/') ticket=$(cat /opt/shell/ticket.txt) curl $pushurl \ -H "Accept: application/json" \ -H 'Content-Type: application/json' \ -d \ '{"ip": "'"${ip}"'", "ticket": "'"${ticket}"'", "data": "'"${msg}"'"}'
架构设计@工程设计@服务稳定性之路
软件开发
2019-11-22 15:10:00
自己的游戏引擎的跨平台工作也进行到一定阶段了,我个人非常注重性能.所以单就性能测试这一块非常注重.这不,再自己的真机上测试了一把,高通骁龙 APQ8064 1.5GHz(4核),2G RAM,同屏10000个Sprite(2D),维持在15-20FPS左右,我个人对这个性能还是比较满意,不知道比起市场同类引擎如何.
软件开发
2014-08-20 14:02:00
在cocos2d-x中集成百度语音识别的时候,运行build_native.py会把libs/armeabi目录清空。
以下是解决办法,把so文件放在jni/prebuilt里面。
修改Android.mk文件,主要有两行 include $(CLEAR_VARS) LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) #百度语音识别so LOCAL_MODULE := BDVoiceRecognitionClient_V1 LOCAL_SRC_FILES := prebuilt/libBDVoiceRecognitionClient_V1.so include $(PREBUILT_SHARED_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := cocos2dcpp_shared
软件开发
2014-08-18 22:21:00
//GameScene.h #include "cocos2d.h" USING_NS_CC; class GameScene : public cocos2d::Layer { public: GameScene(void); ~GameScene(void); static cocos2d::Scene* createScene(); virtual bool init(); virtual void onEnter(); virtual void onEnterTransitionDidFinish(); virtual void onExit(); virtual void onExitTransitionDidStart(); void menuCallback(Ref* pSender); CREATE_FUNC(GameScene); private: //注意不能用auto关键字 Size size; Sprite *sprite; };

//GameScene.cpp //cocos2d: GameScene constructor //cocos2d: GameScene onEnter //cocos2d: GameScene onEnterTransitionDidFinish //cocos2d: HelloWorld constructor //cocos2d: GameScene onExitTransitionDidStart //cocos2d: GameScene onExit //cocos2d: GameScene destructor //cocos2d: HelloWorld onEnter //cocos2d: HelloWorld onEnterTransitionDidFinish //cocos2d: HelloWorld onExitTransitionDidStart //cocos2d: HelloWorld onExit //cocos2d: HelloWorld destructor #include "GameScene.h" #include "HelloWorldScene.h" USING_NS_CC; GameScene::GameScene(void) { log("GameScene constructor"); } GameScene::~GameScene(void) { log("GameScene destructor"); } cocos2d::Scene* GameScene::createScene() { auto scene = Scene::create(); //创建一个场景 auto layer = GameScene::create(); //创建一个图层 scene->addChild(layer); return scene; } //初始化当前的图层 bool GameScene::init() { if(!Layer::init()) //初始化父类 return false; //获取屏幕大小 size = Director::getInstance()->getVisibleSize(); //auto size = Director::getInstance()->getWinSize(); //添加一个图片精灵 sprite = Sprite::create("OnePiece_1.png"); sprite->setPosition(Vec2(size.width/2, size.height/2)); this->addChild(sprite); auto delay = DelayTime::create(2); //回调函数动作的使用!!! sprite->runAction(Sequence::create(delay, CallFuncN::create(CC_CALLBACK_1(GameScene::menuCallback, this)), NULL)); return true; } void GameScene::menuCallback(Ref* pSender) { Director::getInstance()->replaceScene(HelloWorld::createScene()); } void GameScene::onEnter() { Layer::onEnter(); log("GameScene onEnter"); } void GameScene::onEnterTransitionDidFinish() { Layer::onEnterTransitionDidFinish(); log("GameScene onEnterTransitionDidFinish"); } void GameScene::onExit() { Layer::onExit(); log("GameScene onExit"); } void GameScene::onExitTransitionDidStart() { Layer::onExitTransitionDidStart(); log("GameScene onExitTransitionDidStart"); }
软件开发
2014-08-15 19:02:00
由于一直放在收藏夹里,很不方便。于是文章以记录之:
红孩儿的博客(杂): http://blog.csdn.net/honghaier/article/category/1217322
oneRain (C++,2.x): http://blog.csdn.net/oneRain88/article/category/1116401
老G的小屋(Js): http://goldlion.blog.51cto.com/4127613/p-1
liangshaoze (C++,3.X): http://hughie.blog.51cto.com/
dssdss123(C++,2.x): http://blog.csdn.net/dssdss123/article/category/1613103
雪野玲(C++, 3.x): http://my.csdn.net/iamyococo
jyzgo (C++,2.x): http://blog.csdn.net/jyzgo/article/category/1133966
郝萌主 (C++,2.x): http://blog.csdn.net/danielzzu/article/category/1789947/1
qinning199 (C++,2.x): http://blog.csdn.net/qinning199/article/category/1616331
芒果布丁00 (C++, 2.x): http://blog.csdn.net/u013174689/article/category/1825873
Kevin Game (C++, 2.x): http://www.cnblogs.com/KevinYuen/category/458519.html
笨木头(C++ 3.X): http://blog.csdn.net/musicvs/article/category/1213804 yifuteli (lua):http://blog.csdn.net/yifuteli_kevin/article/category/1546107
Jacky's Blog(C++) http://blog.csdn.net/jackystudio
软件开发
2014-05-07 16:47:00
编译protobuffer ,出现了两个问题
1.vs加载项目后,会发现min函数 没有引入头文件 #include
2.vs2013编译测试类的时候,抛出了以下异常:
warning C4996: ‘std::_Copy_impl’: Function call with parameters that may be unsafe – this call relies on the caller to check that the passed values are correct. To disable this warning, use -D_SCL_SECURE_NO_WARNINGS. See documentation on how to use Visual C++ ‘Checked Iterators’
解决的方案请看: http://choorucode.com/2010/08/30/visual-c-c4996-warning-on-copy-with-array-parameters/

protobuffer 中抛出异常的头文件是repeated_field.h中第708行,修改后的代码如下: template void ElementCopier::operator()( Element to[], const Element from[], int array_size) { std::copy(from, from + array_size, stdext::checked_array_iterator(to,array_size)); //std::copy(from, from + array_size, to); }

通过以上修改编译成功


软件开发
2014-05-06 18:01:00
在cmd中执行 python setup.py install 时发生错误,提示缺少ez_setup
在 此 寻找,根据提示:
The recommended way to install setuptools on Windows is to download ez_setup.py and run it.
据此,下载好 ez.setup.py 放到cocos2d的文件夹中,然后编辑文件夹里的setup.py,添加如下两行:
from ez_setup import use_setuptools
use_setuptools
保存
再次在cmd中执行 python setup.py install ,安装成功!
软件开发
2014-02-21 15:17:00
与往常一样,打算如何在游戏是要表现你开始编写任何代码之前! 首先,我们需要一个启动按钮。 我们希望有一个新的气球,每5秒。 每个气球应开始于页面的底部,并移动到页面的顶部。 如果气球越过页面的顶部,那么它的游戏结束了,从舞台上删除所有的气球,并提供了一个重启按钮。
第1步:按钮
我们知道我们有一个开始和重启按钮,我们希望有一个气球,让我们创建这些。我现在用FlashBuilder编辑器。
在 FlashBuilder 中一个新生成的Main.as文件开始,我们嵌入我们的图片: [Embed(source="../lib/start.jpg")] private var StartButton:Class; private var startButton:Sprite = new Sprite(); [Embed(source="../lib/restart.jpg")] private var RestartButton:Class; private var restartButton:Sprite = new Sprite();
我们创建的启动和重启按钮作为精灵的一个实例。我们没有嵌入我们的气球形象,因为我们想要做的,在一个单独的类。
我们已经创建并嵌入我们的按钮。我们可以让他们做一些事情。在init函数,添加下面的代码: //添加启动按钮 startButton.addChild(new StartButton()); startButton.addEventListener(MouseEvent.CLICK, startGame); startButton.buttonMode = true; startButton.x = (stage.stageWidth / 2) - (startButton.width / 2); startButton.y = (stage.stageHeight / 2) - (startButton.height / 2); addChild(startButton);
此代码将开始按钮图像添加到雪碧,并使它这样,当用户点击它,它调用函数'startGame“。我们也让它改变用户的光标,当他们悬停。然后,我们添加我们的启动按钮到舞台的中央!我们现在需要一个叫做startGame功能。 /** * Create some balloons and get them moving! * @param MouseEvent e */ private function startGame(e:MouseEvent):void { startButton.removeEventListener(MouseEvent.CLICK, startGame); removeChild(startButton); }
首先,一些垃圾收集。这只是一些基本的优化。与其等待flash播放器的垃圾回收踢和删除事件侦听器为我们,我们将马上删除并释放一些CPU周期。
我们不希望任何愈显启动按钮,所以我们删除从舞台。
步骤2:气球
现在我们来创建一些气球。
首先,规划。什么是我们希望我们的气球做?
这将是很好,如果气球是不是纯白色。让我们的色彩气球。
他们需要移动。
当用户点击气球,我们要回收。也就是说,使其移动到屏幕的底部,并重新开始它的补间动画到页面的顶部。听起来像一个复位功能给我。
下面是我们的骨骼气球类: /** * Balloon.as * Balloon Class. */ package { import flash.filters.DropShadowFilter; import flash.display.MovieClip; import com.greensock.*; public class Balloon extends MovieClip { [Embed(source = "../lib/balloon.png")] private var BalloonImage:Class; private var myTween:TweenLite; private var colours:Array = [0xFF0000, 0x00FF00, 0x0000FF, 0x006633, 0xFFFCD6, 0x16A5DF]; private var dropShadowEffect:DropShadowFilter; public function Balloon():void { addChild(new BalloonImage()); myTween = new TweenLite(this, 7, {y:-this.height, ease:"Linear.easeNone"}); changeColour(); } public function reset():void { changeColour(); myTween.restart(); } public function die():void { myTween.pause(); } public function changeColour():void{ dropShadowEffect = new DropShadowFilter(0,45,colours[Math.floor(Math.random() * colours.length)],1,width,height,1,1,true,false,false); filters = [dropShadowEffect]; } } }
正如你可以看到我们已经嵌入我们的气球形象在这个类中,并挑选一些鲜艳的颜色来显示它。对于我选择使用气球的移动 Greensock的TweenLite的 ,因为它提供了一个平滑的渐变作用比任何我能想出与。
回到我们的主类,创建一个名为Timer属性 private var timer:Timer = new Timer(5000, -1);
并添加以下到startGame功能: timer.addEventListener(TimerEvent.TIMER_COMPLETE, createBalloon); timer.start(); createBalloon(); score = 0;
这将创建一个新的气球,每5秒。和让球滚动,我们立即Call createBalloon:
private function createBalloon(e:TimerEvent = null):void { var balloon:Balloon = new Balloon(); balloon.addEventListener(MouseEvent.CLICK, popBalloon); balloon.y = stage.stageHeight; balloon.x = Math.floor(Math.random() * (stage.stageWidth - balloon.width)); balloons.push(balloon); addChild(balloon); timer.reset(); timer.start(); }
这个函数创建一个新的气球,沿着屏幕的底部随机位置,并且告诉应用程序,当用户单击新创建的气球运行popBalloon。
private function popBalloon(e:MouseEvent):void { e.target.x = Math.floor(Math.random() * (stage.stageWidth - e.target.width)); e.target.reset(); }
这个函数只是将气球回到屏幕的底部,并告诉它开始再次移动到屏幕的顶部。
如果你在这一点上测试你的游戏,你将有你的游戏的开始!
步骤3:游戏结束并重新启动
您可能已经注意到了,游戏实际上并没有结束。让我们来解决这个问题。当气球在通过屏幕上方,我们将清除屏幕上所有的气球,并显示一个游戏结束的消息。而且,由于游戏喜欢跟踪他们的L33T skilz,我们甚至会告诉他们,他们有多少气球点击。
在Main类中,添加另一个属性: private var score:int;
这将保持跟踪有多少气球已被点击。
更新startGame使分数等于0: score = 0;
最后,更新popBalloon令得分上升:
score++;
有了成绩跟踪,让我们结束这个游戏。更新startGame:
addEventListener(Event.ENTER_FRAME, balloonCheck);
这是什么做的每一帧检查,如果我们的任何气球是在屏幕上方是调用一个函数。
private function balloonCheck(e:Event):void { if (balloons.length) { for (var i:int = 0; i < balloons.length; i++) { if (balloons[i].y == 0 - balloons[i].height) { //game over. removeEventListener(Event.ENTER_FRAME, balloonCheck); for (var i:int = 0; i < balloons.length; i++) { balloons[i].die(); removeChild(balloons[i]); } timer.stop(); return; } } } }
如果任何一个气球上面的屏幕上方是,那么阻止他们移动,从舞台中删除,并停止计时器,否则,我们将最终获得所产生的更多的气球。
现在,我们已经停止了比赛,我们需要向用户显示他们的分数和重启按钮。首先,显示了比分。
在Main类中创建一个新的属性: private var textBox:TextField = new TextField; private var textFormat:TextFormat = new TextFormat(null, 30);
所有的textFormat正在做的是指定的字体大小为30像素。我要申请的textFormat到textBox中的init函数:
textBox.defaultTextFormat = textFormat;
而现在,我们的textBox中添加到舞台。在balloonCheck的部分更新游戏:
textBox.text = "You popped " + score + " balloons!nWell Done!"; textBox.width = textBox.textWidth; textBox.x = (stage.stageWidth / 2) - (textBox.width / 2); textBox.y = (stage.stageHeight / 4); addChild(textBox);
我们添加的重启按钮。下面的代码添加到init函数:
//实例化重启按钮 //instantiate the restart button restartButton.addChild(new RestartButton()); restartButton.buttonMode = true; restartButton.x = (stage.stageWidth / 2) - (restartButton.width / 2); restartButton.y = (stage.stageHeight / 2) - (restartButton.height);
最后,我们调整startGame去除的几行代码到一个新的功能,并创造restartGame,删除了所有的气球从数组并删除按钮和得分。
private function restartGame(e:MouseEvent):void { balloons = []; restartButton.removeEventListener(MouseEvent.CLICK, restartGame); removeChild(restartButton); removeChild(textBox); game(); } private function startGame(e:MouseEvent):void { startButton.removeEventListener(MouseEvent.CLICK, startGame); removeChild(startButton); game(); } private function game():void { addEventListener(Event.ENTER_FRAME, balloonCheck); timer.addEventListener(TimerEvent.TIMER_COMPLETE, createBalloon); timer.start(); createBalloon(); score = 0; }
FINAL的类 package { import flash.display.MovieClip; import flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; import flash.events.TimerEvent; import flash.text.TextField; import flash.text.TextFormat; import flash.utils.Timer; [SWF(width='800',height='600',backgroundColor='#FFFFFF',frameRate='25')] /** * Pop the balloons game. Main Class * @author Richard Parnaby-King */ public class Main extends MovieClip { [Embed(source="../lib/start.jpg")] private var StartButton:Class; private var startButton:Sprite = new Sprite(); [Embed(source="../lib/restart.jpg")] private var RestartButton:Class; private var restartButton:Sprite = new Sprite(); private var balloons:Array = []; private var timer:Timer = new Timer(5000, -1); private var score:int; private var textBox:TextField = new TextField; private var textFormat:TextFormat = new TextFormat(null, 30); public function Main():void { if (stage) init(); else addEventListener(Event.ADDED_TO_STAGE, init); } private function init(e:Event = null):void { removeEventListener(Event.ADDED_TO_STAGE, init); // entry point //add start button startButton.addChild(new StartButton()); startButton.addEventListener(MouseEvent.CLICK, startGame); startButton.buttonMode = true; startButton.x = (stage.stageWidth / 2) - (startButton.width / 2); startButton.y = (stage.stageHeight / 2) - (startButton.height / 2); addChild(startButton); //instantiate the restart button restartButton.addChild(new RestartButton()); restartButton.buttonMode = true; restartButton.x = (stage.stageWidth / 2) - (restartButton.width / 2); restartButton.y = (stage.stageHeight / 2) - (restartButton.height / 2); textBox.defaultTextFormat = textFormat; } private function balloonCheck(e:Event):void { if (balloons.length) { for (var i:int = 0; i < balloons.length; i++) { if (balloons[i].y == 0 - balloons[i].height) { removeEventListener(Event.ENTER_FRAME, balloonCheck); for (var j:int = 0; j < balloons.length; j++) { balloons[j].die(); removeChild(balloons[j]); } timer.stop(); textBox.text = "You popped " + score + " balloons!nWell Done!"; textBox.width = textBox.textWidth; textBox.x = (stage.stageWidth / 2) - (textBox.width / 2); textBox.y = (stage.stageHeight / 4) - (textBox.height / 2); addChild(textBox); restartButton.addEventListener(MouseEvent.CLICK, restartGame); addChild(restartButton); return; } } } } private function restartGame(e:MouseEvent):void { balloons = []; restartButton.removeEventListener(MouseEvent.CLICK, restartGame); removeChild(restartButton); removeChild(textBox); game(); } private function startGame(e:MouseEvent):void { startButton.removeEventListener(MouseEvent.CLICK, startGame); removeChild(startButton); game(); } private function game():void { addEventListener(Event.ENTER_FRAME, balloonCheck); timer.addEventListener(TimerEvent.TIMER_COMPLETE, createBalloon); timer.start(); createBalloon(); score = 0; } private function createBalloon(e:TimerEvent = null):void { var balloon:Balloon = new Balloon(); balloon.addEventListener(MouseEvent.CLICK, popBalloon); balloon.y = stage.stageHeight; balloon.x = Math.floor(Math.random() * (stage.stageWidth - balloon.width)); balloons.push(balloon); addChild(balloon); timer.reset(); timer.start(); } private function popBalloon(e:MouseEvent):void { e.target.x = Math.floor(Math.random() * (stage.stageWidth - e.target.width)); e.target.reset(); score++; } } }
这个打气球的游戏就已经创建完毕了...
软件开发
2014-03-24 12:33:00
public void SendMessage (System.Object param) { try { //这是一个异步的建立连接,当连接建立成功时调用connectCallback方法 clientSocket.BeginConnect (ipAddress, hostPort, new AsyncCallback (connectCallback), param); } catch (Exception ex) { Debug.Log (ex.ToString ()); } } private void connectCallback (IAsyncResult asyncConnect) { if (!clientSocket.Connected) { closeSocket (); } else { clientSocket.EndConnect (asyncConnect); businessComm (asyncConnect.AsyncState); } }
软件开发
2014-04-06 13:04:00
建立一个解释器,对于特定的语言,解释预先定义的文法。解释语言的句子。
场景1:正则表达式解释器
场景2:脚本解释器
场景3:XML解释器
解释器模式提供了一个处理的框架
软件开发
2016-05-07 11:08:00
亲测有效,在安装过程最后一步时,提示do you want to install loader grub时, 选择YES
下一项如果还有GRUB2,选择NO,其他YES,试试能 否成功
如果失败,俩grub都NO,看看能不能行
再不行,GRUB2选择 YES,第一个GRUB选择NO
检查了一下,应该是bios,uefi启动不同的原因,貌似老机器和新机器不一样,最简单的方法如上,只要不两个都选,应该就不会跳ERROR17
软件开发
2016-03-26 12:29:00
协同程序和多线程有相似之处,但是又不是一个东西
协同程序是可以吧一个函数(routine) 分成几段执行,yield 关键字来等待协同程序的返回,
多线程在多核处理器的环境下能真正并行运行,协同程序任意时刻只有一个代码片段在运行
多线程要考虑线程同步,而协同程序不会,因为任意时刻只有一个协同程序的代码片段在运行
协同函数返回值必须是IEnumerator yield return 来返回
注意:4.3.1 最大允许协程数量为10000000个

1.用于定时器 启动1秒后debug输出 void Start () { StartCoroutine(func()); //立即返回 //StartCoroutine("func");//立即返回 } // Update is called once per frame void Update () { } IEnumerator func() { yield return new WaitForSeconds(1);//暂停当前函数片段,让出给其他片段执行,等待WaitForXXXXXX 返回 Debug.Log("1111111"); }
2.加载进度显示 public Text txt; // Use this for initialization bool is_done; int current; int total; void Start() { is_done = false; current = 0; total = 100; txt = GameObject.Find("Text").GetComponent(); StartCoroutine(loding_sync()); // StartCoroutine( ()=>IEnumerator{} ); } // Update is called once per frame void Update() { } IEnumerator loding_sync() { txt.text = "start to loaing"; yield return new WaitForSeconds(1.0f); for (; current <= total; ++current) { yield return new WaitForEndOfFrame(); txt.text = current.ToString() + "%"; dosomethingelse(); } txt.text = "Done"; } void dosomethingelse() { }
软件开发
2016-03-25 13:38:00
更像 订阅,项目需要,所以实现了一个简易的 using UnityEngine; using System.Collections; using System.Collections.Generic; public delegate void HandlerNotifyCenter(System.Object arg); [System.Serializable] class Recver { public string name; public GameObject recver; public HandlerNotifyCenter handler; public Recver() { } public void InVoke(System.Object arg) { handler(arg); } } public class NotifyCenter : MonoBehaviour { static public NotifyCenter ins; List recvers = new List(); void Awake() { ins = this; } // Use this for initialization void Start() { } // Update is called once per frame void Update() { } public void addRecver(GameObject recver, string name, HandlerNotifyCenter handler) { // gameObject.SendMessage(msg); Recver x = new Recver(); x.name = name; x.handler = handler; x.recver = recver; recvers.Add(x); } public void removeRecver(GameObject recver, string name) { foreach (Recver obj in recvers) { if (name == obj.name && recver == obj.recver) { recvers.Remove(obj); } } } public void sendMessage(string msg, System.Object arg) { foreach (Recver obj in recvers) { if (msg == obj.name) { obj.InVoke(arg); } } } }

软件开发
2016-04-10 09:45:00
计算top N words的topology, 用于比如trending topics or trending images on Twitter.

实现了滑动窗口计数和TopN排序, 比较有意思, 具体分析一下代码

Topology

这是一个稍微复杂些的topology, 主要体现在使用不同的grouping方式, fieldsGrouping和globalGrouping

String spoutId = ""; String counterId = ""; String intermediateRankerId = ""; String totalRankerId = ""; builder.setSpout(spoutId, TestWordSpout(), 5); builder.setBolt(counterId, RollingCountBolt(9, 3), 4).fieldsGrouping(spoutId, Fields("")); builder.setBolt(intermediateRankerId, IntermediateRankingsBolt(TOP_N), 4).fieldsGrouping(counterId, Fields("")); builder.setBolt(totalRankerId, TotalRankingsBolt TOP_N)).globalGrouping(intermediateRankerId);

RollingCountBolt
首先使用RollingCountBolt, 并且此处是按照word进行fieldsGrouping的, 所以相同的word会被发送到同一个bolt, 这个field id是在上一级的declareOutputFields时指定的
RollingCountBolt, 用于基于时间窗口的counting, 所以需要两个参数, the length of the sliding window in seconds和the emit frequency in seconds
new RollingCountBolt(9, 3), 意味着output the latest 9 minutes sliding window every 3 minutes
1. 创建SlidingWindowCounter(SlidingWindowCounter和SlotBasedCounter参考下面)
counter = new SlidingWindowCounter(this.windowLengthInSeconds / this.windowUpdateFrequencyInSeconds);
如何定义slot数? 对于9 min的时间窗口, 每3 min emit一次数据, 那么就需要9/3=3个slot
那么在3 min以内, 不停的调用countObjAndAck(tuple)来递增所有对象该slot上的计数
每3分钟会触发调用emitCurrentWindowCounts, 用于滑动窗口(通过getCountsThenAdvanceWindow), 并emit (Map, 实际使用时间)
因为实际emit触发时间, 不可能刚好是3 min, 会有误差, 所以需要给出实际使用时间

2. TupleHelpers.isTickTuple(tuple), TickTuple
前面没有说的一点是, 如何触发emit? 这是比较值得说明的一点, 因为其使用Storm的TickTuple特性.
这个功能挺有用, 比如数据库批量存储, 或者这里的时间窗口的统计等应用
"__system" component会定时往task发送 "__tick" stream的tuple
发送频率由TOPOLOGY_TICK_TUPLE_FREQ_SECS来配置, 可以在default.ymal里面配置
也可以在代码里面通过getComponentConfiguration()来进行配置,
Map getComponentConfiguration() { Map conf = HashMap(); conf.put(Config.TOPOLOGY_TICK_TUPLE_FREQ_SECS, emitFrequencyInSeconds); conf;
配置完成后, storm就会定期的往task发送ticktuple
只需要通过isTickTuple来判断是否为tickTuple, 就可以完成定时触发的功能
isTickTuple(Tuple tuple) { tuple.getSourceComponent().equals(Constants.SYSTEM_COMPONENT_ID) \\ SYSTEM_COMPONENT_ID == "" && tuple.getSourceStreamId().equals(Constants.SYSTEM_TICK_STREAM_ID); \\ SYSTEM_TICK_STREAM_ID == "" }

最终, 这个blot的输出为, collector.emit(new Values(obj, count, actualWindowLengthInSeconds));
obj, count(窗口内的计数和), 实际使用时间

SlotBasedCounter
基于slot的counter, 模板类, 可以指定被计数对象的类型T
这个类其实很简单, 实现计数对象和一组slot(用long数组实现)的map, 并可以对任意slot做increment或reset等操作
关键结构为Map objToCounts, 为每个obj都对应于一个大小为numSlots的long数组, 所以对每个obj可以计numSlots个数
incrementCount, 递增某个obj的某个slot, 如果是第一次需要创建counts数组
getCount, getCounts, 获取某obj的某slot值, 或某obj的所有slot值的和
wipeSlot, resetSlotCountToZero, reset所有对象的某solt为0, reset某obj的某slot为0
wipeZeros, 删除所有total count为0的obj, 以释放空间
SlotBasedCounter Serializable { serialVersionUID = 4858185737378394432L; Map objToCounts = HashMap(); numSlots; SlotBasedCounter( numSlots) { (numSlots <= 0) { IllegalArgumentException("" + numSlots + ""); } .numSlots = numSlots; } incrementCount(T obj, slot) { [] counts = objToCounts.get(obj); (counts == ) { counts = [.numSlots]; objToCounts.put(obj, counts); } counts[slot]++; } getCount(T obj, slot) { [] counts = objToCounts.get(obj); (counts == ) { 0; } { counts[slot]; } } Map getCounts() { Map result = HashMap(); (T obj : objToCounts.keySet()) { result.put(obj, computeTotalCount(obj)); } result; } computeTotalCount(T obj) { [] curr = objToCounts.get(obj); total = 0; ( l : curr) { total += l; } total; } wipeSlot( slot) { (T obj : objToCounts.keySet()) { resetSlotCountToZero(obj, slot); } } resetSlotCountToZero(T obj, slot) { [] counts = objToCounts.get(obj); counts[slot] = 0; } shouldBeRemovedFromCounter(T obj) { computeTotalCount(obj) == 0; } wipeZeros() { Set objToBeRemoved = HashSet(); (T obj : objToCounts.keySet()) { (shouldBeRemovedFromCounter(obj)) { objToBeRemoved.add(obj); } } (T obj : objToBeRemoved) { objToCounts.remove(obj); } } }

SlidingWindowCounter
SlidingWindowCounter只是对SlotBasedCounter做了进一步的封装, 通过headSlot和tailSlot提供sliding window的概念
incrementCount, 只能对headSlot进行increment, 其他slot作为窗口中的历史数据
核心的操作为, getCountsThenAdvanceWindow
1. 取出Map counts, 对象和窗口内所有slots求和值的map
2. 调用wipeZeros, 删除已经不被使用的obj, 释放空间
3. 最重要的一步, 清除tailSlot, 并advanceHead, 以实现滑动窗口
advanceHead的实现, 如何在数组实现循环的滑动窗口
SlidingWindowCounter Serializable { serialVersionUID = -2645063988768785810L; SlotBasedCounter objCounter; headSlot; tailSlot; windowLengthInSlots; SlidingWindowCounter( windowLengthInSlots) { (windowLengthInSlots < 2) { IllegalArgumentException("" + windowLengthInSlots + ""); } .windowLengthInSlots = windowLengthInSlots; .objCounter = SlotBasedCounter(.windowLengthInSlots); .headSlot = 0; .tailSlot = slotAfter(headSlot); } incrementCount(T obj) { objCounter.incrementCount(obj, headSlot); } Map getCountsThenAdvanceWindow() { Map counts = objCounter.getCounts(); objCounter.wipeZeros(); objCounter.wipeSlot(tailSlot); advanceHead(); counts; } advanceHead() { headSlot = tailSlot; tailSlot = slotAfter(tailSlot); } slotAfter( slot) { (slot + 1) % windowLengthInSlots; } }

IntermediateRankingsBolt
这个bolt作用就是对于中间结果的排序, 为什么要增加这步, 应为数据量比较大, 如果直接全放到一个节点上排序, 会负载太重
所以先通过IntermediateRankingsBolt, 过滤掉一些
这里仍然使用, 对于obj进行fieldsGrouping, 保证对于同一个obj, 不同时间段emit的统计数据会被发送到同一个task
IntermediateRankingsBolt继承自AbstractRankerBolt(参考下面)
并实现了updateRankingsWithTuple,
updateRankingsWithTuple(Tuple tuple) { Rankable rankable = RankableObjectWithFields.from(tuple); .getRankings().updateWith(rankable); }
逻辑很简单, 将Tuple转化Rankable, 并更新Rankings列表
参考AbstractRankerBolt, 该bolt会定时将Ranking列表emit出去

Rankable
Rankable除了继承Comparable接口, 还增加getObject()和getCount()接口
Rankable Comparable { Object getObject(); getCount(); }
RankableObjectWithFields
RankableObjectWithFields实现Rankable接口
1. 提供将Tuple转化为RankableObject
Tuple由若干field组成, 第一个field作为obj, 第二个field作为count, 其余的都放到List otherFields中
2. 实现Rankable定义的getObject()和getCount()接口
3. 实现Comparable接口, 包含compareTo, equals
public class RankableObjectWithFields implements Rankable
RankableObjectWithFields from(Tuple tuple) { List otherFields = Lists.newArrayList(tuple.getValues()); Object obj = otherFields.remove(0); Long count = (Long) otherFields.remove(0); RankableObjectWithFields(obj, count, otherFields.toArray()); }
Rankings
Rankings维护需要排序的List, 并提供对List相应的操作
核心的数据结构如下, 用来存储rankable对象的list
List rankedItems = Lists.newArrayList();
提供一些简单的操作, 比如设置maxsize(list size), getRankings(返回rankedItems, 排序列表)
核心的操作是,
updateWith(Rankable r) { addOrReplace(r); rerank(); shrinkRankingsIfNeeded(); }
上一级的blot会定期的发送某个时间窗口的(obj, count), 所以obj之间的排序是在不断变化的
1. 替换已有的, 或新增rankable对象(包含obj, count)
2. 从新排序(Collections.sort)
3. 由于只需要topN, 所以大于maxsize的需要删除
AbstractRankerBolt
首先以TopN为参数, 创建Rankings对象
Rankings rankings; AbstractRankerBolt( topN, emitFrequencyInSeconds) { count = topN; .emitFrequencyInSeconds = emitFrequencyInSeconds; rankings = Rankings(count); }
在execute中, 也是定时触发emit, 同样是通过emitFrequencyInSeconds来配置tickTuple
一般情况, 只是使用updateRankingsWithTuple不断更新Rankings
这里updateRankingsWithTuple是abstract函数, 需要子类重写具体的update逻辑
execute(Tuple tuple, BasicOutputCollector collector) { (TupleHelpers.isTickTuple(tuple)) { emitRankings(collector); } { updateRankingsWithTuple(tuple); } }
最终将整个rankings列表emit出去
emitRankings(BasicOutputCollector collector) { collector.emit( Values(rankings)); getLogger().info("" + rankings); }

TotalRankingsBolt
该bolt会使用globalGrouping, 意味着所有的数据都会被发送到同一个task进行最终的排序.
TotalRankingsBolt同样继承自AbstractRankerBolt
updateRankingsWithTuple(Tuple tuple) { Rankings rankingsToBeMerged = (Rankings) tuple.getValue(0); .getRankings().updateWith(rankingsToBeMerged); }
唯一的不同是, 这里updateWith的参数是个rankable列表, 在Rankings里面的实现一样, 只是多了遍历
最终可以得到, 全局的TopN的Rankings列表



作者


晨色星空J2EE
已学习课程数: 12
已发表笔记数: 51


Ta的笔记

01 awk 实战使用
统计访问日志里面的IP数据,通过IP分类统计出每一个IP最近访问次数,并进行访问次数排序,日志文件格式如下,看似比较混乱的结构:{"referer":"http://www.52ipr.com/index.html","headers":{"X-Proxy":"nginx","Accept":"*/*"},"scheme":"http

02 Linux 学习笔记 Sed命令详解
简介sed 是一种在线编辑器,它一次处理一行内容。处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”(pattern space),接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。文件内容并没有 改变,除非你使用重定向存储输出。Sed主要用来自动编辑一个或多个文件;简化对文件的反复操作;编写转换程序等。 sed使用参数 # sed 选项与参数: -n :使用安静(silent)模式。在一般 sed 的用法中,所有

03 Linux 学习笔记 Grep篇
grep 是一个很常见也很常用的命令,他最重要的功能就是进行字串数据的比对,然后将符合使用者需求的字串列印出来。需要说明的是『grep 在数据中查寻一个字串时,是以 "整行" 为单位来进行数据的撷取的!』也就是说,假如一个文件内有 10 行,其中有两行具有你所搜寻的字串,则将那两行显示在萤幕上,其他的就丢弃了!在关键字的显示方面,grep 可以使用 --color=auto 来将关键字部分使用颜色显示。这可是个很不错的功能啊!但是如果每次使用 grep 都得要自行加上 --color=

04 Spark中文手册 编程指南-3
共享变量一般情况下,当一个传递给Spark操作(例如map和reduce)的函数在远程节点上面运行时,Spark操作实际上操作的是这个函数所用变量的一个独立副本。这些变量被复制到每台机器上,并且这些变量在远程机器上 的所有更新都不会传递回驱动程序。通常跨任务的读写变量是低效的,但是,Spark还是为两种常见的使用模式提供了两种有限的共享变量:广播变量(broadcast variable)和累加器(accumulator)广播变量广播变量允许程序员缓存一个只读的变量在每台机器上面,而不是每个任务保存一份拷

05 Spark中文手册 编程指南-2 弹性分布式数据集 (RDDs)
弹性分布式数据集 (RDDs)Spark 核心的概念是 Resilient Distributed Dataset (RDD):一个可并行操作的有容错机制的数据集合。有 2 种方式创建 RDDs:第一种是在你的驱动程序中并行化一个已经存在的集合;另外一种是引用一个外部存储系统的数据集,例如共享的文件系统,HDFS,HBase或其他 Hadoop 数据格式的数据源。并行集合外部数据集RDD 操作传递函数到 Spark使用键值对TransformationsActionsRDD持久化并行集合并行集合

06 Spark中文手册 编程指南-1
概论在高层中,每个 Spark 应用程序都由一个驱动程序(driver programe)构成,驱动程序在集群上运行用户的mian 函数来执行各种各样的并行操作(parallel operations)。Spark 的主要抽象是提供一个弹性分布式数据集(RDD),RDD 是指能横跨集群所有节点进行并行计算的分区元素集合。RDDs 从 Hadoop 的文件系统中的一个文件中创建而来(或其他 Hadoop 支持的文件系统),或者从一个已有的 Scala 集合转换得到。用户可以要求 Spark 将 R

07 Spark中文手册-快速上手
快速上手本节课程提供一个使用 Spark 的快速介绍,首先我们使用 Spark 的交互式 shell(用 Python 或 Scala) 介绍它的 API。当演示如何在 Java, Scala 和 Python 写独立的程序时,看编程指南里完整的参考。依照这个指南,首先从 Spark 网站下载一个 Spark 发行包。因为我们不会使用 HDFS,你可以下载任何 Hadoop 版本的包。Spark Shell独立应用程序开始翻滚吧!使用 Spark Shell基础Spark 的 shell 作为一

08 Spark 中文手册 总览
Spark 编程指南简体中文版Introduction快速上手Spark Shell独立应用程序开始翻滚吧!编程指南引入 Spark初始化 SparkSpark RDDs并行集合外部数据集RDD 操作传递函数到 Spark使用键值对TransformationsActionsRDD持久化共享变量从这里开始Spark Streaming一个快速的例子基本概念关联初始化StreamingContext离散流输入DStreamsDStream中的转换DStream的输出操作缓存或持久化Checkpointing

09 linux awk命令详解
简介awk是一个强大的文本分析工具,相对于grep的查找,sed的编辑,awk在其对数据分析并生成报告时,显得尤为强大。简单来说awk就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各种分析处理。awk有3个不同版本: awk、nawk和gawk,未作特别说明,一般指gawk,gawk 是 AWK 的 GNU 版本。awk其名称得自于它的创始人 Alfred Aho 、Peter Weinberger 和 Brian Kernighan 姓氏的首个字母。实际上 AWK 的确拥有自己的语

10 linux sort,uniq,cut,wc命令详解
sortsort 命令对 File 参数指定的文件中的行排序,并将结果写到标准输出。如果 File 参数指定多个文件,那么 sort 命令将这些文件连接起来,并当作一个文件进行排序。sort语法# sort 选项与参数: -f :忽略大小写的差异,例如 A 与 a 视为编码相同; -b :忽略最前面的空格符部分; -M :以月份的名字来排序,例如 JAN, DEC 等等的排序方法; -n :使用『纯数字』进行排序(默认是以文字型态来排序的); -r :反向排序; -u :就是


相关笔记

[无]

最新笔记

01 TaoKeeper监控ZK集群
ZK Web界面, node-zk-browserWeb展示每个path的属性和数据。需要安装Node.js 和 node-zookeeper taokeeper为zookeeper做了什么?1.CPU/MEM/LOAD的监控//load2. ZK日志目录所在磁盘剩余空间监控3. 单机连接数的峰值报警4. 单机 Watcher数的峰值报警5. 节点自检:是指对集群中每个IP所在ZK节点上的PATH:

02 java高手养成地:116804208,Java零基础到项目实战在线直播公开课
学习编程并没有那么困难,你只是需要一位大神来手把手来带领,专业的Java老师在线讲课,帮助你有效快速的掌握Java,真心想要学习java的伙伴可以加我们,想学习编程不要看网上的这些视频教程,没有任何意义,你操作过程中会遇到大量的问题,学习编程可以加群【116,804,208】这里有很多人指导你一起学习,Java零基础到项目实战公开课,我们的课程偏向实战性,想要学习Java的伙伴欢迎到我们的课堂来一起学习。

03 awk
类似的数据结构使用awk进行分析。第一种方式假定 ip地址在逗号分隔的第8段中,可以进行如下操作(当然这也的假定条件本身就是有问题的) awk: cat access_log.* | awk -F ',' '{print $8}' | awk -F ':' '{if($1=="\"ip\""){print $2}}' |sort |uniq -c |sort -rn cat

04 awk 实战使用
统计访问日志里面的IP数据,通过IP分类统计出每一个IP最近访问次数,并进行访问次数排序,日志文件格式如下,看似比较混乱的结构:{"referer":"http://www.52ipr.com/index.html","headers":{"X-Proxy":"nginx","Accept":"*/*"},"scheme":"http

05 hive 表跟mysql绑定以后hive表drop不掉解决办法
这个问题网上给出的一般都是修改编码集,但对有些朋友来说,貌似不好使,我自己就是受害者。 原因也确实是编码集,但是很多人像我都搞错了顺序呢!下面是原因以及解决! 我安装mysql的时候默认编码是utf8 ,然后我启动hive 它给我自动create了若干张表,然后我才把表的编码集改成latin,但是其实里面表的编码集还是utf8 导致后面的问题我把mysql下的database

06 CDH 运维笔记
1Hbase日常运维 1.1 监控Hbase运行状况1.1.1操作系统1.1.1.1 IOa.群集网络IO,磁盘IO,HDFS IOIO越大说明文件读写操作越多。当IO突然增加时,有可能:1.compact队列较大,集群正在进行大量压缩操作。2.正在执行mapreduce作业可以通过CDH前台查看整个集群综合的数据或进入指定机器的前台查看单台机器的数据:b.Io wait磁盘IO对集群的影响

07 • Storm优势
• Storm优势  1. 简单的编程模型。类似于MapReduce降低了并行批处 理复杂性,Storm降低了进行实时处理的复杂性。  2. 服务化,一个服务框架,支持热部署,即时上线或下线App.  3. 可以使用各种编程语言。你可以在Storm之上使用各种 编程语言。默认支持Clojure、Java、Ruby和Python。要 增加对其他语言的支持,只需实现一个简单的Storm通信 协议即可。  4. 容错性。Storm会管理工作进程和节点的故障。  5. 水平扩展。计算是在多个线程、进程和

08 如何在LATEX里高亮显示R代码
转自 如何在Latex中高亮显示R语言代码,请看下面的示例:\documentclass{beamer}
软件开发
2016-04-02 12:57:00
一. iTween 介绍 、 二. iTween 原理、 三. iTween 下载、 四. iTween 类介绍、 五.主要功能介绍

原文地址: http://blog.csdn.net/dingkun520wy/article/details/50550529

一. iTween 介绍
iTween 是一个动画库 , 作者创建它的目的就是最小的投入实现最大的产出 . 让你做开发更轻松 , 用它可以轻松实现各种动画 , 晃动 , 旋转 , 移动 , 褪色 , 上色 , 控制音频等等

二. iTween 原理
iTween 的核心是数值插值,简单说就是给 iTween 两个数值 ( 开始值,结束值 ) ,它会自动生成一些中间值,大概像这样子 , 开始值 -> 中间值 -> 中间值 …. -> 结束值。
这里的数值可以理解为 : 数字,坐标点,角度,物体大小,物体颜色,音量大小 等

三. iTween 下载
从官网 http://itween.pixelplacement.com 下载

四. iTween 类介绍
iTween类的公共操作接口均以静态方法的形式提供。可分为三大类:
1,静态注册方法 :提供注册动画效果的静态方法接口。如:MoveTo、CameraFadeTo等。
2,Update静态方法 :提供每帧改变属性值的环境,在Update或循环环境中调用。如:MoveUpdate、AudioUpdate等。
3,外部工具方法 :包括动画控制、路径绘制等。
iTween类内部定义了三种枚举类型:
1,EaseType :缓动类型枚举
2,LoopType :动画的循环类型枚举
3,NamedValueColor :已命名颜色枚举

五.主要功能介绍
8种动画方法:
1,Fade: 淡入淡出 详情 http://blog.csdn.net/dingkun520wy/article/details/50923665
2,Look: 旋转对象使其面朝指定位置 ,详解 http://blog.csdn.net/dingkun520wy/article/details/50578142
3,Move: 移动位置,详解 http://blog.csdn.net/dingkun520wy/article/details/50476864
4,Rotate: 旋转指定欧拉角度 详解 http://blog.csdn.net/dingkun520wy/article/details/50696489
5,Scale: 缩放大小,详解 http://blog.csdn.net/dingkun520wy/article/details/50684392
6,Punch: 添加摇晃力 详解 http://blog.csdn.net/dingkun520wy/article/details/50828042
7,Shake: 摆动对象 详情 http://blog.csdn.net/dingkun520wy/article/details/50836780
8,CameraFade: 摄像机的淡入淡出 详情 http://blog.csdn.net/dingkun520wy/article/details/50896420

2种音频方法:
1,Audio: 音量和音调的变化 详情 http://blog.csdn.net/dingkun520wy/article/details/50826033
2,Stab : 播放AudioClip一次,不用手动加载AudioSource组件 详情 http://blog.csdn.net/dingkun520wy/article/details/50826033

1种颜色变化方法:
1,Color: 变换颜色 详情 http://blog.csdn.net/dingkun520wy/article/details/51065275

1种值变化方法:
1,ValueTo: 返回一个“from”和“to”之间的插值,用以改变属性, 详解 http://blog.csdn.net/dingkun520wy/article/details/50550527

每种动画又有一种或多种执行方式 :To(从原始到目标)、From(从目标到原始)、Add(随时间改变,根据提供的量)、By(增加提供的量)。

每个方法一般有两种重载方式 :最小定制选项、完全定制项。

Update类方法 :提供每帧改变属性值的环境。在Update或FixedUpdate方法或类似于循环的环境中调用。

动画控制 : Pause(暂停),Resume(恢复),Stop(停止并销毁iTween)

绘制方法 :DrawLine(绘制线条),DrawLineGizmos(绘制线条),DrawPath(绘制弯曲的路径)DrawPathGizmos(与DrawPath相同)

其他方法 :Count(返回iTween的数量),PathLength(返回路径长度),PutOnPath(根据提供的百分比将物体放置于所提供路径上),PointOnPath(返回路径上指定百分比处的Vector3)

i TweenPath类 :用于在Scene试图中编辑路径。 详情介绍 : http://blog.csdn.net/dingkun520wy/article/details/51075774
软件开发
2016-02-22 21:39:00
在手机游戏当中,游戏的资源加密保护是一件很重要的事情。
我花了两天的时间整理了自己在游戏当中的资源加密问题,实现了跨平台的资源流加密,这个都是巨人的肩膀之上的。
大概的思路是这样的,游戏资源通过XXTEA加密方法对流的加密方式,有自己的密钥和标识,通过标识可知是否有加密,密钥是自己程序当中的。除非有密钥,否则很难通过解出正确的文件。经过加密后,加密文件也就是游戏资源放在resource的自己文件夹中,否则在xcode编译到趁机是会识别不了文件。在程序中cocos2dx底层加入解密过程,就可以把文件正确读取出来,让程序显示。经试验,已经可以读取,png,plist,json文件。
现在记录下实现的步骤
链接: http://download.csdn.net/detail/dingkun520wy/9268409 去下载加密资源的脚本,这个是 quick-cocos2d-x提取出来的打包工具。
pack_files.sh -i olddir -o newdir -ek XXTEA -es decodetest
把ResourcesDecode和xxtea四个文件加入到cocos2dx/platform下;
把platform/ResourcesDecode.cpp \
platform/xxtea.c \加入到cocos2dx/platform的android.mk文件中,加入android编译。
写一个单例用来保存密码和对流解密过程,代码如下: CCAssert(buf != NULL, "decodeData buf not NULL"); unsigned char* buffer = NULL; ResourcesDecode* decode = ResourcesDecode::sharedDecode(); bool isXXTEA = decode && decode->m_xxteaEnabled; for (unsigned int i = 0; isXXTEA && i < decode->m_xxteaSignLen && i < size; ++i) { isXXTEA = buf[i] == decode->m_xxteaSign[i]; } if (isXXTEA) { //decrypt XXTEA xxtea_long len = 0; buffer = xxtea_decrypt(buf+decode->m_xxteaSignLen, (xxtea_long)size -(xxtea_long)decode->m_xxteaSignLen, (unsigned char*)decode->m_xxteaKey, (xxtea_long)decode->m_xxteaKeyLen, &len); delete [] buf; buf = NULL; size = len; } else { buffer = buf; } if (pSize) { *pSize = size; } return buffer;

?
buffer就是经过XXTEA解密后正确的流。
在 CCFileUtils ::getFileData()当中return返回之前调用解密pBuffer = ResourcesDecode :: sharedDecode ()-> decodeData (pBuffer, size, pSize);这里是跨平台的读取资源的方法。
在 ZipFile ::getFileData()当中也加入解密方法pBuffer = ResourcesDecode :: sharedDecode ()-> decodeData (pBuffer, fileInfo. uncompressed_size , pSize);这个是android读取plist的地方,我也不太清楚为什么android会在这里读取资源。
在bool CCSAXParser ::parse( const char *pszFile)中把原先的rt改为rb : char * pBuffer = ( char *) CCFileUtils :: sharedFileUtils ()-> getFileData (pszFile, /*"rt"*/ "rb" , &size);
ios的修改地方 不一样
在CCFileUtilsIOS中的 createCCDictionaryWithContentsOfFile修改如下,注释掉的是原先的,后面是新增的。
? CCDictionary* CCFileUtilsIOS::createCCDictionaryWithContentsOfFile(const std::string& filename) { std::string fullPath = CCFileUtils::sharedFileUtils()->fullPathForFilename(filename.c_str()); // NSString* pPath = [NSString stringWithUTF8String:fullPath.c_str()]; // NSDictionary* pDict = [NSDictionary dictionaryWithContentsOfFile:pPath]; unsigned long fileSize = 0; unsigned char* pFileData = CCFileUtils::sharedFileUtils()->getFileData(fullPath.c_str(), "rb", &fileSize); NSData *data = [[[NSData alloc] initWithBytes:pFileData length:fileSize] autorelease]; delete []pFileData; NSPropertyListFormat format; NSString *error; NSMutableDictionary *pDict = (NSMutableDictionary *)[ NSPropertyListSerialization propertyListFromData:data mutabilityOption:NSPropertyListMutableContainersAndLeaves format:&format errorDescription:&error];


在CCImage.mm当中修改,同样是注释是原先的,后面是新增的。
static bool _initWithFile(const char* path, tImageInfo *pImageinfo) { CGImageRef CGImage; UIImage *jpg; UIImage *png; bool ret; // convert jpg to png before loading the texture // NSString *fullPath = [NSString stringWithUTF8String:path]; // jpg = [[UIImage alloc] initWithContentsOfFile: fullPath]; unsigned long fileSize = 0; unsigned char* pFileData = cocos2d::CCFileUtils::sharedFileUtils()->getFileData(path, "rb", &fileSize); NSData *adata = [[NSData alloc] initWithBytes:pFileData length:fileSize]; delete []pFileData; jpg = [[UIImage alloc] initWithData:adata];



android平台
在CCImageCommon_cpp当中修改如下
? bool CCImage::initWithImageFileThreadSafe(const char *fullpath, EImageFormat imageType) { bool bRet = false; unsigned long nSize = 0; #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) CCFileUtilsAndroid *fileUitls = (CCFileUtilsAndroid*)CCFileUtils::sharedFileUtils(); // unsigned char *pBuffer = fileUitls->getFileDataForAsync(fullpath, "rb", &nSize); unsigned char* pBuffer = CCFileUtils::sharedFileUtils()->getFileData(fullpath, "rb", &nSize);

到此,基本结束了。
在自己程序当中加入资源前把设置密钥和标识和自己加密资源时的一样: ResourcesDecode :: sharedDecode ()-> setXXTeaKey ( "XXTEA" , strlen ( "XXTEA" ), "decodetest" , strlen ( "decodetest" ));
其它就正常的读取和显示。
参考网址:http://my.oschina.net/SunLightJuly/blog/184061
http://my.oschina.net/SunLightJuly/blog/189971
http://my.oschina.net/SunLightJuly/blog/184179
软件开发
2016-02-22 21:40:00
using UnityEngine; using System.Collections; public class NewBehaviourScript : MonoBehaviour { Canvas canvas; RectTransform rectTransform; // Use this for initialization void Start () { rectTransform = transform as RectTransform; canvas = GameObject.Find("Canvas").GetComponent(); } // Update is called once per frame void Update () { Vector2 pos; if(RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas.transform as RectTransform, Input.mousePosition, canvas.worldCamera, out pos)){ rectTransform.anchoredPosition = pos; } } }
软件开发
2016-03-11 12:17:00
我是用Cocostudio做的界面,但是背景音乐是用代码来加载的,开始的时候还好好的,后来突然背景音乐就没有了。想了好多方法,也去百度了,但是没有找到解决方案。具体的表现就是刚进入界面的时候会有一秒左右的背景音乐出现但是之后就没有了,我吧代码有撸了一遍发现没有调用stop方法,这就蛋疼了,音乐有没法下断点调试,,,
后来没办法,干脆注释掉所有的代码只留加载背景音乐的部分,发现背景音乐可以播放。之后再一段一段的往里面添加剩下的代码。发现加载csb文件的时候背景音乐会停止,然后我就把背景音乐的代码挪到紧接着加载rootNode的csb文件的后面,这样问题就解决了,但是具体问题原因我还是不明白,之前放代码的时候也是在加载csb之后的,只不过不在紧接着的地方,那时候就没有声音,但是不管了,问题解决了。
PS:还有一种方法,只是这个在我这个界面里面没有什么效果。就是把加载声音的代码写在onEnter()方法里。这个方法在init之后调用,我有的地方也是用这个来写的。
软件开发
2016-01-09 19:07:06

相信大家有些人对OpenGL的模板缓冲区不是很理解,包括我最开始也是,OpenGL的模板缓冲区其实就是采用过滤的技术来控制那些颜色可以绘制,那些不能进行绘制。这里的过滤技术也就是我们的一个控制方法,主要体现在如下两个函数glStencilFunc(GLenum func,GLint ref,GLuint mask)和glStencilOp(GLenum fail,GLenum zfail, GLenum zpass),其中
1.glStencilFunc中的第一个参数指的是过滤函数,(如何来进行过滤),过滤函数有如下几种类型
GL_NEVER 从来不能通过
GL_ALWAYS 永远可以通过(默认值)
GL_LESS 小于参考值可以通过
GL_LEQUAL 小于或者等于可以通过
GL_EQUAL 等于通过
GL_GEQUAL 大于等于通过
GL_GREATER 大于通过
GL_NOTEQUAL 不等于通过
在这里“通过”的意思指的是,我们在将图元绘制到帧缓冲区的时候在片段进行测试的时候是可以完全透过去的,否则的话这个片段就无法绘制到对应的颜色帧缓冲区,那么我们所绘制的内容也就显示不出来。通过这种控制方法来控制显示,其实这种操作在我们实际的生活中也是很常见的,例如给汽车喷漆,盖章(只会显示刻了的内容)。
2.通过模板操作glStencil()来控制模板结果值的操作,例如,如果失败了对模板值进行加1,减1等处理。等待下一次片段处理的时候再进行新的比较,对值的过滤做新的控制。
3.在这里我想通过这样一个例子来说明一下: [cpp] view plain copy #include #include #include #include #pragma comment(lib, "glut32.lib") // #include // #pragma comment(lib, "glew32.lib") void init() { glClearColor(0.0, 0.0, 1.0, 0.0); glClearStencil(0); glEnable(GL_STENCIL_TEST); } void display() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); glLoadIdentity(); glTranslatef(0.0, 0.0, -20.0); glStencilFunc(GL_ALWAYS, 0, 0x00); //glStencilFunc(GL_NEVER, 0x0, 0x0); //glStencilOp(GL_INCR, GL_INCR, GL_INCR);// glColor3f(1.0f, 1.0f, 1.0f); float dRadius = 5.0 * (sqrt(2.0) / 2.0); glBegin(GL_LINE_STRIP); for ( float dAngel = 0; dAngel < 380.0; dAngel += 0.1) { glVertex2d(dRadius * cos(dAngel), dRadius * sin(dAngel)); dRadius *= 1.003; } glEnd(); //glStencilFunc(GL_NOTEQUAL,0x1,0x1); //glStencilOp(GL_INCR,GL_INCR,GL_INCR);// glColor3f(1.0f, 0.0f, 0.0f); glRectf(-5, -5, 5, 5); glutSwapBuffers(); } void reshape( int w, int h) { glViewport(0, 0, w, h); float aspect = (w * 1.0) / h; glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60, aspect, 1, 100); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } int main( int argc, char * argv[]) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_STENCIL); glutInitWindowPosition(200,200); glutInitWindowSize(600,600); glutCreateWindow(argv[0]); // assert(GLEW_NO_ERROR == glewInit()); init(); glutReshapeFunc(reshape); glutDisplayFunc(display); glutMainLoop(); return 0; }
加入模板控制之后的结果: [cpp] view plain copy #include #include #include #include #pragma comment(lib, "glut32.lib") // #include // #pragma comment(lib, "glew32.lib") void init() { glClearColor(0.0, 0.0, 1.0, 0.0); glClearStencil(0); glEnable(GL_STENCIL_TEST); } void display() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); glLoadIdentity(); glTranslatef(0.0, 0.0, -20.0); //glStencilFunc(GL_ALWAYS, 0, 0x00); glStencilFunc(GL_NEVER, 0x0, 0x0); glStencilOp(GL_INCR, GL_INCR,GL_INCR); // glColor3f(1.0f, 1.0f, 1.0f); float dRadius = 5.0 * (sqrt(2.0) / 2.0); glBegin(GL_LINE_STRIP); for ( float dAngel = 0; dAngel < 380.0; dAngel += 0.1) { glVertex2d(dRadius * cos(dAngel), dRadius * sin(dAngel)); dRadius *= 1.003; } glEnd(); glStencilFunc(GL_NOTEQUAL, 0x1, 0x1); glStencilOp(GL_INCR, GL_INCR, GL_INCR); // glColor3f(1.0f, 0.0f, 0.0f); glRectf(-5.0, -5.0, 5.0, 5.0); glutSwapBuffers(); } void reshape( int w, int h) { glViewport(0, 0, w, h); float aspect = (w * 1.0) / h; glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60.0, aspect, 1.0, 100.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } int main( int argc, char * argv[]) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_STENCIL); glutInitWindowPosition(200, 200); glutInitWindowSize(600, 600); glutCreateWindow(argv[0]); // assert(GLEW_NO_ERROR == glewInit()); init(); glutReshapeFunc(reshape); glutDisplayFunc(display); glutMainLoop(); return 0; }
软件开发
2016-01-02 21:49:00
Storm实时分析平台的起源,Storm分布式集群实施
流式处理场景:算法交易
storm组件
1.nimbus集群中心,控制节点
2.supervisor nodes taskernode
3.the zookeeper cluster 交换信息
storm数据模型:由许多记录组成,送入
环境准备
1.关闭防火墙
2.修改hosts
192.168.88.197 user5.hadoop.com
192.168.88.198 user6.hadoop.com
192.168.88.196 user7.hadoop.com
wget http://mirror.bit.edu.cn/apache/zookeeper/zookeeper-3.3.6/zookeeper-3.3.6.tar.gz
tar -zxvf zookeeper-3.3.6.tar.gz
cd zookeeper-3.3.6
mkdir data
cd conf/
cp -rp zoo_sample.cfg zoo.cfg
dataDir=/root/soft/zookeeper-3.3.6/data
前一个端口选举,后一个端口zookeeper进行通讯
server.1=user5.hadoop.com:2888:3888
server.2=user6.hadoop.com:2888:3888
server.3=user7.hadoop.com:2888:3888
cd ~
scp -rp zookeeper-3.3.6/ root@ip:/home/grid
[root @localhost data]# cd /root/soft/zookeeper-3.3.6/data
创建myid
在dataDir(/home/grid/zookeeper-3.4.6/data)中创建一个myid
因为server.1=user5.hadoop.com:2888:3888指定的是1,所以在user5.hadoop.com的
机器上echo “1” >myid
其余两台机器配置,user6.hadoop.com myid=2 user7.hadoop.com myid=3
/root/soft/zookeeper-3.3.6/bin
[root @localhost bin]#
[root @localhost bin]# ./zkServer.sh start
JMX enabled by default
Using config: /root/soft/zookeeper-3.3.6/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
[root @localhost bin]# ./zkServer.sh status
JMX enabled by default
Using config: /root/soft/zookeeper-3.3.6/bin/../conf/zoo.cfg
Mode: leader
Using config: /root/soft/zookeeper-3.3.6/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
[root@localhost bin]# ./zkServer.sh status
JMX enabled by default
Using config: /root/soft/zookeeper-3.3.6/bin/../conf/zoo.cfg
Mode: follower
[root@localhost bin]# ./zkServer.sh start
JMX enabled by default
Using config: /root/soft/zookeeper-3.3.6/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
[root@localhost bin]# ./zkServer.sh status
JMX enabled by default
Using config: /root/soft/zookeeper-3.3.6/bin/../conf/zoo.cfg
Mode: follower
安装python
https://www.python.org/downloads/ftp/python2.6.6
python
[root@localhost Desktop]# tar -zxf Python-2.7.2.tgz
[root@localhost soft]# cd Python-2.7.2
./configure --prefix=/usr/local/python27
make
make install
[root@localhost Python-2.7.2]# ls /usr/local/python27/ -al
total 28
drwxr-xr-x 6 root root 4096 Feb 5 23:29 .
drwxr-xr-x 12 root root 4096 Feb 5 23:28 ..
drwxr-xr-x 2 root root 4096 Feb 5 23:29 bin
drwxr-xr-x 3 root root 4096 Feb 5 23:29 include
drwxr-xr-x 4 root root 4096 Feb 5 23:29 lib
drwxr-xr-x 3 root root 4096 Feb 5 23:29 share
[root@localhost Python-2.7.2]# mv /usr/bin/python /usr/bin/python_old
[root@localhost Python-2.7.2]# ln -s /usr/local/python27/bin/python /usr/bin/
[root@localhost Python-2.7.2]# python
Python 2.7.2 (default, Feb 5 2016, 23:28:47)
[GCC 4.1.2 20080704 (Red Hat 4.1.2-46)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>
[root@localhost Python-2.7.2]# vi /usr/bin/yum
#!/usr/bin/python2.4
0.9之前的安装步骤
同样安装JDK和python,zookeeper集群
2.和前面一样,只是要多安装storm的两个依赖库,zeroMQ和JZMQ
1.搭建zookeper集群
2.安装storm依赖库【JDK和python,zeroMQ,JZMQ】
3.下载并且解压storm发布版本
4.修改storm.yaml文件
5.启动storm后台进程
[root@localhost soft]# wget http://mirror.bit.edu.cn/apache/storm/apache-storm-0.9.1-incubating/apache-storm-0.9.1-incubating.tar.gz
[root@localhost soft]# tar -zxf apache-storm-0.9.1-incubating.tar.gz
t@localhost soft]# tar -zxf apache-storm-0.9.1-incubating.tar.gz
@localhost soft]# mv apache-storm-0.9.1-incubating storm-0.9.1
[root@localhost soft]# cd storm-0.9.1
[root@localhost storm-0.9.1]# cd conf
[root@localhost conf]# ls
[root@localhost soft]# cd storm-0.9.1
[root@localhost storm-0.9.1]# cd conf
[root@localhost conf]# ls
storm.yaml
[root@localhost conf]# vi storm.yaml
storm.zookeeper.servers:
- "192.168.88.197"
- "192.168.88.198"
- "192.168.88.196"
#
nimbus.host: "192.168.88.197"
storm.local.dir:"/root/soft/storm-0.9.1/data"
supervisor.slots.ports:
-6700
-6701
-6702
-6703

/root/soft/storm-0.9.1
[root@localhost soft]# scp -rp storm-0.9.1/ root@user6.hadoop.com:~/soft
[root@localhost soft]# scp -rp storm-0.9.1/ root@user7.hadoop.com:~/soft
.BASH_PROFILE
STORM_HOME=/root/soft/storm-0.9.1
PATH=$PATH:$HOME/bin:$STORM_HOME/bin
export STORM_HOME最下面
[root@localhost storm-0.9.1]# source /etc/profile
[root@localhost storm-0.9.1]# echo $PATH
/root/soft/storm-0.9.1/bin
[root@localhost ~]# jps确保三台都能够正常启动
5215 QuorumPeerMain
23327 Jps
[root@localhost conf]# storm nimbus &在nimbus节点
[root@localhost conf]# jps
5536 nimbus
5609 Jps
5431 QuorumPeerMain
[root@user6 ~]# storm supervisor &
[root@user6 ~]# jps
\5165 QuorumPeerMain
24473 Jps
24354 supervisor

[root@localhost conf]# jps
5536 nimbus
5609 Jps
5431 QuorumPeerMain
[root@localhost conf]# storm logviewer &
[root@localhost conf]# storm ui &网址8080端口
[root@localhost conf]# jps
5536 nimbus
5841 logviewer
6013 core
5431 QuorumPeerMain
6074 Jps

[root@user6 ~]# jps
5165 QuorumPeerMain
24678 Jps
24354 supervisor

安装git
yum install git或者 http://distfiles.macports.org/git-core/git-1.8.4.2.tar.gz

wget http://mirrors.ustc.edu.cn/fedora/epel/epel-release-latest-5.noarch.rpm

安装maven
wget http://mirrors.hust.edu.cn/apache/maven/maven-3/3.2.5/binaries/apache-maven-3.2.5-bin.tar.gz
解压配置环境变量
[root@localhost soft]# git clone git://github.com/nathanmarz/storm-starter.git
[root@localhost soft]# cd storm-starter
[root@localhost storm-starter]# ls
LICENSE multilang README.markdown storm-starter.iml
m2-pom.xml project.clj src test
[root@localhost storm-starter]# mv m2-pom.xml pom.xml
[root@localhost storm-starter]# vi pom.xml

org.apache.storm
storm-core
0.9.1-incubating

provided


[root@localhost storm-starter]# mvn compile exec:java -Dstorm.topology=storm.starter.WordCountTopology
mvn package
storm jar storm-starter-0.0.1-SNAPSHOT-jar-with-dependencies.jar storm.starter.RollingTopwords
远程模式
storm jar storm-starter-0.0.1-SNAPSHOT-jar-with-dependencies.jar storm.starter.ExclamationTopology ExclamationTopology
storm kill ExclamationTopology
安装ZeroMQ
安装依赖包
yum install gcc-c++ libuuid-devel
对于libuuid-devel
先安装ncurses-5.9.tar
在下载ftp://ftp.kernel.org/pub/linux/utils/util-linux/v2.21/util-linux-2.21.1.tar.gz
编译安装
下载http://download.zeromq.org/zeromq-2.1.7.tar.gz
编译安装JZMQ
git clone git://github.com/nathanmarz/jzmq.git
cd jzmq
[root@localhost jzmq]# ./autogen.sh
[root@localhost jzmq]# ./configure
make
make install
下载并解压storm第三台
yum install unzip
wget https://github.com/downloads/nathanmarz/storm/storm-0.8.1.zip
ocalhost soft]# unzip storm-0.8.1.zip
[root@localhost soft]# cd storm-0.8.1
cd storm-0.8.1
修改conf/storm.yaml文件在配置的每一项前加空格,冒号后也要加空格
storm.zookeeper.servers:
- "192.168.88.197"
- "192.168.88.198"
- "192.168.88.199"
#
nimbus.host: "192.168.88.199"
storm.local.dir: "/root/soft/storm-0.8.1/data"
supervisor.slots.ports:
- 6700
- 6701
- 6702
- 6703
[root@localhost conf]# jps
5215 QuorumPeerMain
13883 Jps
修改环境变量
[root@localhost ~]# storm nimbus &
[1] 13974
[root@localhost ~]# jps
[root@localhost ~]# jps
14127 Jps
13974 nimbus
5215 QuorumPeerMain
14037 logviewer
14059 core
mvn compile exec:java -Dstorm.topology=storm.starter.WordCountTopology
[root@localhost storm-starter]# mvn package
[root@localhost storm-starter]# cd target
[root@localhost target]# storm jar storm-starter-0.0.1-SNAPSHOT-jar-with-dependencies.jar storm.starter.RollingTopWords本地
[root@localhost target]# storm jar storm-starter-0.0.1-SNAPSHOT-jar-with-dependencies.jar storm.starter.ExclamationTopology ExclamationTopology集群方式


软件开发
2016-02-06 22:00:00
Unity 游戏开发技巧集锦之创建部分光滑部分粗糙的材质
创建部分光滑部分粗糙的材质
生活中,有类物体的表面既有光滑的部分,又有粗糙的部分,例如丽江的石板路,如图 3-17 所示,石板的表面本来是粗糙的,但是在石板上面走的人多了,石板的一部分就变得光滑了。有时,游戏为了显得更加逼真,就需要模拟这样一种材质。
图 3-17 兼具光滑和粗糙表面的丽江石板路
要制作部分光滑部分粗糙的材质,需要用到两种资源:拥有镜面着色器的材质和模拟了现实状况的纹理。
Unity中 创建并配置材质
在 Project 视图里,创建一个材质,并命名为 rustyMetalMaterial ,选中它然后在 Inspector 视图里修改 Shader 属性为 Specular ,如图 3-18 所示。
图 3-18 设置材质的 Shader 属性
在此种 Shader 属性下,会出现两个重要的属性: q Specular Color :可以调节镜面所反射的光的颜色。 q Shininess :可以调节镜面所反射的光的强度。
制作兼具光滑和粗糙效果的纹理
本小节使用的纹理,模拟的是生了锈的金属表面,部分光滑部分因为有铁锈而显得粗糙,如图 3-19 所示。本小节打算为这个纹理做些简单的修饰:添加透明度的信息;以及在纹理上写些文字,用来衬托出光滑和粗糙位置处的不同效果。
图 3-19 拥有生绣的金属表面效果的纹理
使用 PhotoShop 打开此纹理,然后依次做下面的操作:
( 1 )在通道窗口下,新建一个通道,使用默认的名称 Alpha 1 。拷贝蓝色通道上的图像到 Alpha 1 通道,并设置 Alpha 1 通道上图像的亮度和对比度,如图 3-20 所示。通过此种方式,设置了这样的透明信息:铁锈处是完全不透明的,而光滑处是完全透明的。
图 3-20 设置 Alpha 1 通道上图像的亮度和对比度
( 2 )切换到图层视图,然后为此图像添加黄色的文字,文本内容随意。然后设置文字图层为叠加效果,不透明度为 80% ,如图 3-21 所示,对比了文字设置前后,效果的差异。这样的话,文字看起来就是写在金属表面上,而非铁锈上了。
图 3-21 设置文字图层为叠加效果,不透明度为 80%
( 3 )合并文字图层和图像图层后,存储此文件,接下来会导入到 Unity 中使用。
部分光滑部分粗糙的材质的 效果展示
将上一小节制作的纹理导入到 Unity 中,然后选中 Project 视图里的 rustyMeterial ,在 Inspector 视图里设置它的 Base(RGB)Gloss(A) 属性为导入的纹理,最后就可以在 Inspector 视图的预览窗口中看到效果了,如图 3-22 所示。
图 3-22 材质效果展示
本文选自: Unity 游戏开发技巧集锦大学霸内部资料,转载请注明出处,尊重技术尊重IT人!
软件开发
2015-09-11 15:45:00
Unity 2D游戏开发教程之精灵的死亡和重生
精灵的死亡和重生
目前为止,游戏项目里的精灵只有 Idle 和 Walking 这两种状态。也就是说,无论精灵在游戏里做什么,它都不会进入其它的状态,如死亡。于是我们发现游戏里的精灵,即使是跳入“万丈深渊”,也依然存活,显然这种游戏逻辑无法让人接受。因此,本节就来说明为精灵添加死亡和重生这两种状态的方法,并使用脚本实现这两种状态的逻辑。具体的实现步骤如下:
( 1 )在 Hierarchy 视图里,新建一个 Empty 对象,并命名为 Death Trigger ,设置其 Position 属性为 (0,0,0) 。然后为此对象添加 Box Collider 2D 组件,并设置此组件的下列属性,如图 2-6 所示。 q 选中 Is Trigger 属性; q Size : (20,1) ; q Center : (0,-2.5) ;

图 2-6 对象上 Box Collider 2D 组件的属性设置
回看此时的 Scene 视图,可知此步操作添加了一个绿色线框的矩形,如图 2-7 所示。我们希望当精灵与此矩形发生接触时,精灵会死亡。
图 2-7 表示 Empty 对象范围的矩形框
( 2 )当精灵死亡以后,要想继续游戏,精灵必须在指定的位置重生才行,而且这个位置在精灵对象重生以后,不会让精灵被动的接触到 Death Trigger 对象。在 Hierarchy 视图里,再新建一个 Empty 对象,并命名为 Player Respawn Point ,设置其 Position 属性为 (0,1,5,0) ,也就是说重生的点位于地面正上方的指定位置处,如图 2-8 所示。
图 2-8 设置精灵重生点的位置
( 3 )打开 Project 视图里的 PlayerStateController 脚本,将死亡和重生这两种状态加到表示精灵状态的枚举类型中,如下代码中加粗的部分: 01 using UnityEngine; 02 using System.Collections; 03 04 public class PlayerStateController : MonoBehaviour 05 { 06 // 定义游戏人物的各种状态 07 public enum playerStates 08 { 09 idle = 0, // 表示空闲 10 left, // 表示左移 11 right, // 表示右移 12 kill, // 表示死亡 13 resurrect // 表示重生 14 } 15 … // 省略 16 }
( 4 )在 Project 视图的 Script 文件夹里,新建一个 C# 脚本,命名为 DeathTriggerScript ,用于实现当精灵与 Death Trigger 接触时,精灵死亡的逻辑。为此脚本添加下面的代码: 01 using UnityEngine; 02 using System.Collections; 03 04 public class DeathTriggerScript : MonoBehaviour 05 { 01 // 当精灵进入到 Death Trigger 的矩形范围内时,调用此函数 02 void OnTriggerEnter2D( Collider2D collidedObject ) 03 { 04 // 调用精灵对象上 PlayerStateListener 脚本组件里的 hitDeathTrigger() 方法 05 collidedObject.SendMessage("hitDeathTrigger"); 06 } 07 }
将此脚本赋予 Hierarchy 视图里的 Death Trigger 对象。脚本 05 行,调用的方法 hitDeathTrigger() 还没有在 PlayerStateListener 脚本里定义,请将下面的方法定义添加到 PlayerStateListener 脚本里,定义如下: 01 using UnityEngine; 02 using System.Collections; 03 04 [RequireComponent(typeof(Animator))] 05 public class PlayerStateListener : MonoBehaviour 06 { 07 … // 省略 08 public void hitDeathTrigger() 09 { 10 onStateChange(PlayerStateController.playerStates.kill); 11 } 12 }
从方法的定义中可知,它所实现的功能是,修改精灵当前的状态为 Kill 。
( 5 )继续为脚本 PlayerStateListener 添加代码,用于实现当精灵处于死亡和重生状态时,精灵应有的动作,或者说行为,部分脚本 PlayerStateListener 的代码如下: 01 using UnityEngine; 02 using System.Collections; 03 04 [RequireComponent(typeof(Animator))] 05 public class PlayerStateListener : MonoBehaviour 06 { 07 // 公有属性 08 public float playerWalkSpeed = 3f; // 表示精灵移动的速度 09 public GameObject playerRespawnPoint = null; // 表示重生的点 10 // 私有属性 11 private Animator playerAnimator = null; // 表示对象上的 Animator 组件 12 … // 省略 13 // 用于检测当前所处的动画状态,在不同的状态下将表现出不同的行为 14 void onStateCycle() 15 { 16 // 表示当前对象的大小 17 Vector3 localScale = transform.localScale; 18 // 判断当前处于何种状态 19 switch(currentState) 20 { 21 … // 省略 22 case PlayerStateController.playerStates.kill: 23 onStateChange(PlayerStateController.playerStates.resurrect); 24 break; 25 26 case PlayerStateController.playerStates.resurrect: 27 onStateChange(PlayerStateController.playerStates.idle); 28 break; 29 } 30 } 31 // 当角色的状态发生改变的时候,调用此函数 32 public void onStateChange(PlayerStateController.playerStates newState) 33 { 34 // 如果状态没有发生变化,则无需改变状态 35 if(newState == currentState) 36 return; 37 // 判断精灵能否由当前的动画状态,直接转换为另一个动画状态 38 if(!checkForValidStatePair(newState)) 39 return; 40 // 通过修改 Parameter 中 Walking 的值,修改精灵当前的状态 41 switch(newState) 42 { 43 … // 省略 44 case PlayerStateController.playerStates.kill: 45 break; 46 // 让精灵在场景重生对象的位置出现 47 case PlayerStateController.playerStates.resurrect: 48 transform.position = playerRespawnPoint.transform.position; 49 transform.rotation = Quaternion.identity; 50 51 break; 52 } 53 // 记录角色当前的状态 54 currentState = newState; 55 } 56 57 // 用于确认当前的动画状态能否直接转换为另一动画状态的函数 58 bool checkForValidStatePair(PlayerStateController.playerStates newState) 59 { 60 bool returnVal = false; 61 62 // 比较两种动画状态 63 switch(currentState) 64 { 65 … // 省略 66 // 精灵的 kill 状态只能转换为 resurrect 状态 67 case PlayerStateController.playerStates.kill: 68 if(newState == PlayerStateController.playerStates.resurrect) 69 returnVal = true; 70 else 71 returnVal = false; 72 break; 73 // 精灵的 resurrect 状态只能转换为 idle 状态 74 case PlayerStateController.playerStates. resurrect : 75 if(newState == PlayerStateController.playerStates.idle) 76 returnVal = true; 77 else 78 returnVal = false; 79 break; 80 } 81 return returnVal; 82 } 83 public void hitDeathTrigger() 84 { 85 onStateChange(PlayerStateController.playerStates.kill); 86 } 87 }
对于此脚本,有以下几点需要说明: q 脚本 09 行,添加了一个公有属性,用于表示游戏场景里 Player Respawn Point 对象的位置。这个属性的值需要在 Inspector 视图里设置,如图 2-9 所示。
图 2-9 设置 Player State Listener 脚本组件里的 Player Respawn Point 属性值 q 脚本 14 行,方法 onStateCycle() 里添加的代码,说明当精灵进入到 kill 状态以后,接着会进入 resurrect 状态;而进入 resurrect 状态的精灵会接着进入 idle 状态; q 脚本 32 行,方法 onStateChange() 里添加的代码,说明当精灵处于 resurrect 状态时,精灵将会出现在重生点的位置; q 脚本 58 行,方法 checkForValidStatePair() 里添加的代码,说明处于 kill 状态的精灵只能转换为 resurrect 状态;而处于 resurrect 状态的精灵只能转换为 idle 状态;

( 6 )为脚本 CameraController 添加处理精灵 kill 和 resurrect 状态的代码,如下: 01 using UnityEngine; 02 using System.Collections; 03 04 public class CameraController : MonoBehaviour 05 { 06 … // 省略 07 void onStateCycle() 08 { 09 switch(currentPlayerState) 10 { 11 … // 省略 12 case PlayerStateController.playerStates.kill: 13 trackPlayer(); 14 break; 15 case PlayerStateController.playerStates.resurrect: 16 trackPlayer(); 17 break; 18 } 19 } 20 }
( 7 )运行游戏,控制精灵移动至地面外,精灵在下落的过程中与 Death Trigger 发生接触,精灵死亡;很快的,精灵会在 Player Respawn Point 对象的位置处重生,如图 2-10 所示。
图 2-10 精灵的死亡和重生
本文选自: Unity 2D游戏开发快速入门大学霸内部资料,转载请注明出处,尊重技术尊重IT人!
软件开发
2015-09-07 10:19:00
https://github.com/cloudwu/skynet
skynet 整体代码分为3个部分: 1: skynet-src 核心网络层 2: service-src 服务层 3: service 使用lua实现的服务
skynet 实现是一套Actor模式的网络架构,Actor 中核心的概念包括: Worker 工作执行器 Actor 封装了上下文的相关服务实现 master 调度分配器
在skynet的核心网络层中,主要包含: worker的实现 module的加载, 用于支持actor的实现 网络分发和监听 以及相关worker的调度
module加载采用dlsym 的字符串加载方式,优点便于配置,缺点阅读代码查找相关实现比较麻烦。
module加载的service_snlua 服务将用于 加载执行lua代码,并将上下文环境保存到lua虚拟机中,从而构成一个Actor
在skynet_start.c 中的 thread_worker 是工作执行器的实现,
通过从消息队列中获取消息,并将消息分发出去交给service来执行。
当没有消息的时候 worker线程将会等在一个条件变量上。
分配器的实现
Skynet 存在一个全局消息队列的队列 global_queue,每个worker 从 global_queue 中获取一个消息队列,接着执行当前消息队列的上下文中的回调函数。
skynet_context_new 函数用于分配一个新的上下文,并且生成一个新的消息队列,而每个上下文相当于一个Actor, 而上下文对应的消息队列 相当于一个 mailbox, 这也就解释了 在 thread_worker 线程中为什么依次去 取不同的消息队列的消息,防止某个Actor被饿死。
在skynet 存在一个 command_func cmd_funcs 里面包含了所有的服务器命令,总共18条,分别为 { "TIMEOUT", cmd_timeout } { "REG", cmd_reg }, { "QUERY", cmd_query }, { "NAME", cmd_name }, { "NOW", cmd_now }, { "EXIT", cmd_exit }, { "KILL", cmd_kill }, { "LAUNCH", cmd_launch }, { "GETENV", cmd_getenv }, { "SETENV", cmd_setenv }, { "STARTTIME", cmd_starttime }, { "ENDLESS", cmd_endless }, { "ABORT", cmd_abort }, { "MONITOR", cmd_monitor }, { "MQLEN", cmd_mqlen }, { "LOGON", cmd_logon }, { "LOGOFF", cmd_logoff }, { "SIGNAL", cmd_signal },
当某处代码调用LAUNCH 命令的时候就会新建一个上下文,同时分配一个新的消息队列给该上下文,这样Worker就可以执行消息的分发了。
而skynet_send 以及 skynet_sendname 函数实现了向消息队列投递消息;
这样整个调度基本流程就清晰了: 首先通过LAUNCH 命令启动一个上下文,创建一个消息队列 接着通过skynet_send 向消息队列中投递消息 Worker线程在合适的时机将消息分发给对应上下文的 处理函数
启动流程
在skynet_start 启动函数中, 通过执行 snlua bootstrap 命令,执行系统初始化
snlua bootstrap表示执行 bootstrap.lua 脚本来具体初始化。
bootstrap 中将会注册需要的一些 lua服务。
而调用 skynet_socket_init 将会启动socket服务器
消息的分类
skynet中消息分为两类: 服务器内部各个Actor之间互相投递消息 通过socket 消息投递
thread_socket 为socket线程主循环,主要函数是skynet_socket_poll ,
存在两个方向数据:即从socket接受到的网络数据,和从内部向外发送的网络数据。 从网络接受的数据通过 forward_message 推送给合适的消息队列,通过 skynet_context_push 函数。 发送给网络的数据 skynet_socket_send 接口来发送
skynet_socket是一个基础,在之上构建的服务实例也是一个Actor, 例如service_gated 服务, 以及lua的 gateserver 服务。
这样当内部服务需要通过socket向外部发送消息的时候,首先将message 投递给gate,接着由gate来调用实际的socket接口向外发送消息。
一个例子如下: 首先启动gate 客户端连接gate gate 根据消息创建对应的 Actor Agent 来处理 Agent 可以通过gate 来向对应客户端投递消息, 客户端消息也可以通过gate 传递给agent
Actor 的实现
lua层中通过实例化一个服务来构建一个新的lua状态机,从而构建一个独立的Actor。 lua中的接口为 skynet.newservice 最终调用skynet_context_new 来构建新的Actor。
每当socket 需要接受或者发送数据,或者timer定时器时间到了,都会调用wakeup, 这样worker线程就会去获取消息队列数据. 系统存在一个Monitor线程, 用于监控所有其它线程,包括socket线程,timer线程,worker线程。
Timer 实现
skynet_timer 有一个 skynet_timeout API, 在skynet_server 中存在一个 TIMEOUT命令,当接受到TIME_OUT命令的时候,就会增加一个新的timer。
lua层接口为 skynet.timeout。
Timer主要应用: 某些Actor需要定期执行,通过skynet.timeout 创建一个协程定期调用函数。
lua协程
skynet lua接口中有一个 co_create 用于创建协程。在以下几种情况下创建: 增加一个timeout 定时器, 定时后执行协程 fork 一个函数,用于创建一个新的 协程,将协程加入到fork_queue中,在下次分发消息的时候执行该协程 raw_dispatch_message 当分发消息的时候将会将对应的消息处理函数放入协程中
协程可以等在特定的事件上,当事件发生的时候,唤醒对应协程的执行,这个通过一个suspend函数实现的,将session, 事件以及对应的协程保存起来,当事件发生,找到对应的协程,接着执行协程即可。
skynet的协议以及投递消息寻址方式
skynet 中发送消息的接口主要两个 skynet_send 和skynet_sendname.
lua层接口 skynet.send
发送消息的时候一般需要提供,当前发送服务的上下文,消息投递来源,消息目标服务接受者,消息类型,消息会话id,消息数据和消息数据大小。
参考样例:watchdog.lua 和agent.lua
在watchdog.lua中,close_agent 调用skynet.send(目标服务对象agent, 协议类型lua, 数据内容disconnect)
这样就通过lua打包协议,将disconnect 字符串发送给了 一个agent服务对象。
在agent.lua中,skynet.start( skynet.dispatch("lua" ))
对通过lua协议分发来的消息,注册相关的处理函数。在skynet.lua 的 raw_dispatch_message 函数中,当收到新的消息,则根据协议类型lua 找到处理函数.
而agent.lua 中调用skynet.ret()含义为,将函数执行的结果按原路yield返回给请求方。
而调用agent.lua 的分发处理函数的 raw_dispatch_message 中以协程方式调用该函数,因此在suspend 函数中将会收到,RETURN 命令,而将函数调用结果返回给watchdog.lua
在watchdog.lua 的socket.OPEN 函数中,调用了skynet.call 这个接口和skynet.send不同在于call 需要等待请求的服务方的返回。
skynet.call实现使用了 yield_call函数,将会将目标服务和session匹配起来,接着yield一个call命令, 当对方服务的RESPONSE 发送回来的时候,会带上相关的会话id,接着在raw_dispatch_message 中检测到协议为 RESPONSE, 根据会话id 去除watchdog.lua 的挂起的协程,继续执行watchdog的协程。
skynet 的网络模型
skynet 底层只有skynet_send 标准接口用于向目标投递消息,不同的通讯模型建立在这个基础之上。 request response lua层skynet.call 将启动一个会话,并将lua协程等在该会话上,当response发送回来之后则重启协程执行 request lua 层skynet.send 直接调用c接口,发送数据包 push 每当skynet raw_dispatch_message 时,都会启动一个协程来处理对应的消息, 也就是每个消息对应一个协程, 在同一个Actor中可以同时存在多个协程
skynet 消息类型 协议类型
软件开发
2015-08-20 13:07:00
SDL是一个游戏开发要用到的东东 翻墙出去淘到了一些好货。就是是英文的小伙伴们有没有英文好的 一起翻译出来
今天把这个文档放在了网上,地址是: http://jivin.3vfree.net/index.php.html
软件开发
2015-08-10 22:21:00
1. 事件管理机制 1.1 在2.2.2版中分散的事件分发器cc.TouchDispatcher, cc.MouseDispatcher, cc.KeyboardDispatcher, cc.AccelerometerDispatcher的所有功能都已经被合并到cc.eventManager,所以事件(鼠标,触摸,键盘,陀螺仪, 用户自定义)都将由cc.eventManager负责分发,也都将通过它进行注册。
更多关于cc.eventManager的信息可以查看这篇 详细文档 1.2 由于新的事件管理机制支持开发者在任何对象上绑定事件,所以在2.2.2版中的cc.Layer的事件处理相关函数都被删除了,具体被删除的函数列表如下: isMouseEnabledsetMouseEnabledsetMousePrioritygetMousePriorityregisterWithTouchDispatcherisTouchEnabledsetTouchEnabledgetTouchPrioritysetTouchPrioritygetTouchModesetTouchModeisAccelerometerEnabledsetAccelerometerEnabled (被移到cc.inputManager中) --> cc.inputManager.setAccelerometerEnabledsetAccelerometerInterval (被移到cc.inputManager中) --> cc.inputManager.setAccelerometerIntervalisKeyboardEnabledsetKeyboardEnabledsetKeypadEnabled
2. 游戏创建和配置流程 2.1 游戏创建
在3.0 alpha版本中, cc.Application 和 cc.AppControl 已经被移除了。
取而代之,我们重构了整个游戏创建的流程,在3.0中游戏项目创建将变得前所未有的简单,开发者可以使用 cc.game 来创建并开始游戏。 cc.game.onStart = function(){ cc.director.runScene(new MyScene());};cc.game.run();
更详细的信息请参见这个文档: cc.game 2.2 游戏配置
在2.2.2版中,游戏的配置列表(包含renderMode, fps...)与初始化代码混在cocos2d.js文件中,这样很不美观,所以3.0版中我们移除了 cocos2d.js 文件并将配置列表单独抽出来放在了 project.json 中,所以修改配置非常简单直观。
所有配置项可以参见这篇文档: project.json 2.3 Cocos2d-html5模块配置
Cocos2d-html5已经成为了在2d游戏开发的各个方面都非常有竞争力的游戏引擎,引擎拥有覆盖面非常广泛的特性。也正因此,我们的引擎比 市面上大多数的2d游戏引擎占用空间要更大。假设开发者只需要引擎中一部分特性,并且希望引擎占有空间更小,3.0版中我们提供了按需定制引擎的功能。首 先,引擎被分割成了不同的模块,所有模块定义可以参见 cocos2d-html5/moduleConfig.json ,开发者可以在 project.json 的 modules 字段中指定自己需要的模块。默认情况下, cocos2d 是默认模块,它包含完整的Cocos2d-html5,开发者可以将它替换为自己需要的子模块。
moduleConfig.json文档
3. 资源加载过程 3.1 cc.loader
cc.Loader 和 cc.FileUtils 已经被 cc.loader 单例对象所取代。 // 设定图片资源路径cc.loader.resPath = "./res";// 设定音频资源路径cc.loader.resPath = "./audio";// 加载资源并获得回调cc.loader.load(res, function(err){ if(err) return console.log("load failed");});
详细文档请参见: cc.loader 3.2 资源加载工具
3.0版不仅提供了更统一易用的cc.loader,还提供了一些配套工具: 异步函数调用模块(模仿node.js): cc.async 资源路径配置工具: cc.path
4. 属性风格API 4.1 使用Javascript风格的API对对象属性进行直接存取
旧API 新API
node.setPosition(x, y); node.setRotation(r);
node.x = x; node.y = y; node.rotation = r;
如表格所示,函数调用可以被属性的直接賦值所替代。在3.0版中不仅是 x , y 和 rotation ,cc.Node及其所有子类的属性都可以使用这种方式存取。 4.2 attr 函数
3.0版同时还提供了一个更加强大的对象配置方法:类似jQuery的 attr 函数可以让你一次性配置多个属性。 node.attr({ x: 20, y: 20, anchorX: 0.5, anchorY: 0.5, width: 400, height: 300, scale: 2});
详细文档和具体属性列表参见: Property API
5. 基本数据结构重构 移除的API: cc.integerToColor3Bcc.c4FFromccc3Bcc.c4FFromccc4Bcc.c4BFromccc4Fcc.PointSpritecc.GridSizecc.gcc.V2F_C4F_T2Fcc.V2F_C4F_T2F_Quad 修改的API: cc.Color3B, cc.Color4B, cc.Color4F --> cc.Colorcc.c3b, cc.c4b, cc.c4f --> cc.colorcc.c3BEqual, cc.c4FEqual --> cc.colorEqualcc.convertColor3BtoHexString --> cc.colorToHexcc.convertHexNumToColor3B --> cc.hexToColorcc.white --> cc.color.whitecc.yellow --> cc.color.yellowcc.blue --> cc.color.bluecc.green --> cc.color.greencc.red --> cc.color.redcc.magenta --> cc.color.magentacc.black --> cc.color.blackcc.orange --> cc.color.orangecc.gray --> cc.color.gray
详细文档
6. 单例对象
3.0版将以前的C++风格单例类重构为Javascript对象,方便开发者的使用。下面是被重构的类列表: // 引擎核心类cc.AudioEngine.getInstance() --> cc.audioEnginecc.Configuration.getInstance() --> cc.configurationcc.Configuration.purgeConfiguration() removedcc.Director.getInstance() --> cc.directorcc.EGLView.getInstance() --> cc.viewcc.ShaderCache.getInstance() --> cc.shaderCachecc.TextureCache.getInstance() --> cc.textureCachecc.TextureCache.purgeSharedTextureCache() --> cc.textureCache._clear()cc.AnimationCache.getInstance() --> cc.animationCachecc.AnimationCache.purgeSharedAnimationCache() --> cc.animationCache._clear()cc.SpriteFrameCache.getInstance() --> cc.spriteFrameCachecc.SpriteFrameCache.purgeSharedSpriteFrameCache() --> cc.SpriteFrameCache._clear()cc.SAXParser.getInstance() --> cc.saxParsercc.PlistParser.getInstance() --> cc.plistParsercc.Screen.getInstance() --> cc.screencc.TIFFReader.getInstance() --> cc.tiffReadercc.IMEDispatcher.getInstance() --> cc.imeDispatcher// 扩展包中的类ccs.GUIReader.getInstance() --> ccs.guiReaderccs.GUIReader.purge() --> ccs.guiReader.clear()ccs.SceneReader.getInstance() --> ccs.sceneReaderccs.SceneReader.purge() --> ccs.sceneReader.clear()ccs.DataReaderHelper --> ccs.dataReaderHelperccs.DataReaderHelper.purge() --> ccs.dataReaderHelper.clear()ccs.SpriteFrameCacheHelper.getInstance() --> ccs.spriteFrameCacheHelperccs.SpriteFrameCacheHelper.purge() --> ccs.spriteFrameCacheHelper.clear()ccs.ArmatureDataManager.getInstance() --> ccs.armatureDataManagerccs.ArmatureDataManager.purge() --> ccs.armatureDataManager.clear()ccs.ActionManager.getInstance() --> ccs.actionManagerccs.ActionManager.purge() --> ccs.actionManager.clear()ccs.TriggerMng.getInstance() --> ccs.triggerManagerccs.ObjectFactory.getInstance() --> ccs.objectFactory
详细文档 。
7. [Alpha 2新添加] 对象创建与类的继承
在Cocos2d-html5 2.2.x中,创建一个引擎对象比如cc.Sprite,开发者需要使用正确的 create 函数: var sprite = cc.Sprite.create(filename, rect);var sprite = cc.Sprite.createWithTexture(texture, rect);var sprite = cc.Sprite.createWithSpriteFrameName(spriteFrameName);
在Cocos2d-JS v3.0 alpha中,我们做到一个非常重要的API进化,所有 createXXX 都被合并为统一的 create 函数: var sprite = cc.Sprite.create(filename, rect);var sprite = cc.Sprite.create(texture, rect);var sprite = cc.Sprite.create("#" + spriteFrameName);
这个改动不仅适用于cc.Sprite,同样适用于引擎中所有有类似API的类,支持的类列表以及关于 create 函数改造的更详细信息请参见 create API文档 。
我们从未停止改进我们的引擎,所以在Cocos2d-JS v3.0 alpha2中,引擎支持 new 直接构造对象!构造函数和 create 函数共享完全相同的参数: var sprite = new cc.Sprite(filename, rect);var sprite = new cc.Sprite(texture, rect);var sprite = new cc.Sprite("#" + spriteFrameName);
与此同时,为了向后兼容性,所有 create 函数也被保留,使用哪种API风格完全是开发者自由的选择。更重要的是,这个改进使得类的继承变得前所未有的简单。开发者现在可以完全忽略所有的 initXXX 函数,你可以简单得通过重载 ctor 函数并使用正确的参数调用 this._super 即可完成对象的初始化: var Enemy = cc.Sprite.extend({ hp: 0, fileName: "enemy.png" ctor: function (hp) { this._super(fileName); this.hp = hp; }});var enemy1 = new Enemy(100);
如上所示,一个 init 函数都不需要调用,非常便于使用。所有cocos2d和扩展类都被重构以支持这种风格,而且JSB也同样完美支持。详细内容请参考关于 new 对象构造和类的继承的 详细文档 。
8. GUI控件 8.1 Cocostudio扩展包中的GUI控件已经被移出单独作为独立的扩展包:ccui,所以所有这些控件类的命名空间都从 ccs. 变为 ccui. 。这样做的原因在于这些UI控件不仅可以被Cocostudio使用,也可以被不使用Cocostudio的开发者单独使用。下面是所有被重命名的类: ccs.Layout --> ccui.Layoutccs.Margin --> ccui.Marginccs.MarginZero --> ccui.MarginZeroccs.LayoutParameter --> ccui.LayoutParameterccs.RelativeLayoutParameter --> ccui.RelativeLayoutParameterccs.CocosGUIVersion --> ccui.cocosGUIVersionccs.UIHelper --> ccui.helperccs.Widget --> ccui.Widgetccs.Button --> ccui.Buttonccs.CheckBox --> ccui.CheckBoxccs.ImageView --> ccui.ImageViewccs.LoadingBar --> ccui.LoadingBarccs.Slider --> ccui.Sliderccs.Text --> ccui.Textccs.TextAtlas --> ccui.TextAtlasccs.TextBMFont --> ccui.TextBMFontccs.TextField --> ccui.TextFieldccs.UILayer --> deleted 8.2 除此之外,3.0版还提供了一个新的富文本控件 ccui.RichText . 8.3 ccs.UILayer 已经从v3.0a中删除,Widget对象要加到场景中,直接通过addChild加到Node节点中就可以了。示例如下: // v2.2.2用法: widget必须要通过UILayer的addWidget方法加入到UILayer之后,再将UILayer加入场景才行var uiLayer = ccs.UILayer.create();uiLayer.addWidget(aWidget);var node = cc.Node.create();node .addChild(uiLayer);...//v3.0a用法: widget可以直接调用node的addChild方法,就可以加入场景了。var node = cc.Node.create();node .addChild(aWidget);
9. NodeGrid
3.0版提供了一个新的节点 cc.NodeGrid ,这个节点可以包含一个目标节点并允许在这个目标节点上应用 ActionGrid类型的动作。在2.2.2版中cc.Node可以直接应用这种动作,但是这个行为会在未来版本中被移除,因为我们希望cc.Node 的逻辑可以更纯粹。下面是2.2.2版与3.0版中的ActionGrid动作使用示例比较: // 2.2.2版var shaky = cc.Shaky3D.create( duration, cc.size(15,10), 5, false );var sprite = cc.Sprite.create();sprite.runAction( shaky );// 3.0版var shaky = cc.Shaky3D.create( duration, cc.size(15,10), 5, false );var sprite = cc.Sprite.create();var nodeGrid = cc.NodeGrid.create();nodeGrid.addChild( sprite );nodeGrid.runAction( shaky );
注意:在Cocos2d-html5 3.0a版中,第一种方式仍然有效,但是如果你希望你的游戏可以运行在JSB中,那么必须使用第二种方式。另外,在3.0正式版中,第一种方式也将被移除。
10. JSB相关
虽然我们尽力使Cocos2d-html5和Cocos2d-JSB的API趋于一致,但是我们发现Web应用开发者和JSB原生开发者需求还是有 一定的区别,有一些需求也很难在两个不同平台上完全融合起来,所以我们提供下面这些仅在JSB项目中支持的API,如果你需要使用它们,请首先进行平台检 查。 if (cc.sys.isNative) { cc.fileUtils.isFileExist("filename");} 10.1 C++宏定义
在JSB项目中,有一些宏定义只可能在C++代码中修改,这些宏定义如下,它们都可以在ccMacros.h或ccConfig.h中找到: CC_ENABLE_STACKABLE_ACTIONSCC_ENABLE_GL_STATE_CACHECC_FIX_ARTIFACTS_BY_STRECHING_TEXELCC_DIRECTOR_STATS_INTERVALCC_DIRECTOR_STATS_POSITIONCC_DIRECTOR_FPS_POSITIONCC_DIRECTOR_DISPATCH_FAST_EVENTSCC_DIRECTOR_MAC_USE_DISPLAY_LINK_THREADCC_NODE_RENDER_SUBPIXELCC_SPRITEBATCHNODE_RENDER_SUBPIXELCC_TEXTURE_ATLAS_USE_VAOCC_USE_LA88_LABELSCC_SPRITE_DEBUG_DRAWCC_SPRITEBATCHNODE_DEBUG_DRAWCC_LABELBMFONT_DEBUG_DRAWCC_LABELATLAS_DEBUG_DRAWCC_NODE_DEBUG_VERIFY_EVENT_LISTENERSCC_ENABLE_PROFILERSCC_LUA_ENGINE_DEBUGCC_USE_PHYSICSCC_ENABLE_SCRIPT_BINDING 10.2 [Alpha 2新添加] cc.fileUtils
在Cocos2d-html5中,cc.FileUtils已经被cc.loader取代了,但是在JSB项目中,有一些需求cc.loader无 法满足,所以我们决定将cc.FileUtils作为仅JSB支持的API开放出来。并且为了符合新的单例对象API风格,开发者可以直接通过 cc.fileUtils 来获取cc.FileUtils单例对象。下面是详细API列表: cc.fileUtilscc.fileUtils.fullPathForFilename(filename)cc.fileUtils.loadFilenameLookup(filename)cc.fileUtils.getStringFromFile(filename)cc.fileUtils.getByteArrayFromFile(filename) // [beta新添加]cc.fileUtils.createDictionaryWithContentsOfFile(filename) // [beta新添加]cc.fileUtils.isAbsolutePath(path)cc.fileUtils.isPopupNotify()cc.fileUtils.getValueVectorFromFile(filename)cc.fileUtils.writeToFile(object, filename)cc.fileUtils.getValueMapFromFile(filename)cc.fileUtils.isFileExist(filename)cc.fileUtils.purgeCachedEntries()cc.fileUtils.fullPathFromRelativeFile(filename, relativeFile)cc.fileUtils.getWritablePath()cc.fileUtils.addSearchPath(path) // [beta新添加]cc.fileUtils.setSearchPaths(pathArray) // [beta新添加]cc.fileUtils.getSearchPaths() // [beta新添加]cc.fileUtils.setSearchResolutionsOrder(orderArray) // [beta新添加]cc.fileUtils.getSearchResolutionsOrder() // [beta新添加]
请注意关于搜索路径的函数的使用,因为它们会导致在Cocos2d-html5和Cocos2d-JSB中资源路径的不一致,而最终使得游戏代码较 难维护。如果需要使用,我们建议在Web端和JSB中使用两套不同的资源映射表,同样的资源变量对应不同的资源路径,这样可以较轻松得维护代码。 10.3 cc.AssetsManager
cc.AssetsManager是用于管理和使用远程服务器资源的类,它也支持简单的版本控制和更新。下面是它的使用方式: var manager = new cc.AssetsManager(manifestPath, storagePath);// As the process is asynchronised, you need to retain the assets manager to make sure it won't be released before the process is ended.manager.retain();if (!manager.getLocalManifest().isLoaded()) { cc.log("Fail to update assets, step skipped.");}else { var listener = new cc.EventListenerAssetsManager(manager, function(event) { switch (event.getEventCode()) { case cc.EventAssetsManager.UPDATE_PROGRESSION: var percent = event.getPercent(); cc.log("Download percent : " + percent); break; case cc.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST: case cc.EventAssetsManager.ERROR_PARSE_MANIFEST: cc.log("Fail to download manifest file, update skipped."); break; case cc.EventAssetsManager.ALREADY_UP_TO_DATE: case cc.EventAssetsManager.UPDATE_FINISHED: cc.log("Update finished."); // You need to release the assets manager while you are sure you don't need it any more manager.release(); break; case cc.EventAssetsManager.ERROR_UPDATING: cc.log("Asset update error: " + event.getAssetId() + ", " + event.getMessage()); break; default: break; } }}
更多信息请参考 cc.AssetsManager文档 .
11. 其他API变动 11.1 cc.Broswser 和 sys 被 cc.sys 取代: 详细文档 . 11.2 一些 cc.AudioEngine 的API被删除: preloadMusicpreloadEffectisFormatSupportedpreloadSound 11.3 cc.SAXParser
一些 cc.SAXParser 的API被删除: tmxParsepreloadPlistunloadPlistgetNamegetExtgetList
同时添加 cc.PlistParser 用于解析plist文件: cc.SAXParser文档 11.4 cc.textureCache 的 addImageAsync 方法被移除,请统一使用 addImage . addImage(url) --> addImage(url)addImageAsync(url, target, callback) --> addImage(url, callback, target)
[Alpha 2新添加] 新的 addImage 使用方式也被JSB支持了。 11.5 MenuItemFont 的两个方法被重命名以适应统一的API风格: fontName --> getFontNamefontSize --> getFontSize 11.6 cc.view
3.0版已经支持所有苹果设备的视网膜屏分辨率,你可以使用 cc.view.enableRetina(enableOrNot) 来开启或关闭这项功能,你也可以使用 cc.view.isRetinaEnabled() 来检测当前视网膜屏适配是否已经开启。最后,你可以通过 cc.view.getDevicePixelRat io() 来获取视网膜屏的像素缩放比例,在目前的苹果设备上,该比例返回值为2。默认情况下,视网膜屏适配在苹果设备上自动开启,如果希望改变这一行为,在关闭这项功能之后,你将需要调用一次 cc.view.setDesignResolutionSize(width, height, policy) 来让改变生效。
[Alpha 2新添加] cc.view会在移动浏览器上自动尝试进入全屏。现在我们为这项功能添加了开关函数,默认情况下这项功能仍然是开启的。 cc.view.enableAutoFullScreen(enabled); // enabled参数值可以是true或falsecc.view.isAutoFullScreenEnabled(); // 该函数返回当前值 11.7 其他被删除的API cc.IS_SHOW_DEBUG_ON_PAGEcc.COCOS2D_DEBUGcc.PLATFORM_WINDOWScc.PLATFORM_LINUXcc.PLATFORM_MACOScc.PLATFORM_ANDROIDcc.PLATFORM_IPHONEcc.PLATFORM_BLACKBERRYcc.PLATFORM_NACLcc.PLATFORM_EMSCRIPTENcc.HASH_FIND_INTcc.isAddedHiddenEventcc.originalCanvasSizecc.configcc.loadImgcc.loadImage.handlercc.computeImageFormatTypecc.tgaLoadccs.UILayer 11.8 其他添加的API: cc.warncc.errorcc.defineGetterSettercc.BuilderReader.registerController 11.9 其他修改的API: cc.Assert --> cc.assertcc.ArrayVerifyType --> cc.arrayVerifyTypecc.ArrayRemoveObject --> cc.arrayRemoveObjectcc.ArrayRemoveArray --> cc.arrayRemoveArraycc.ArrayAppendObjectsToIndex --> cc.arrayAppendObjectsToIndexcc.ArrayRemoveObjectAtIndex(arr, index) --> arr.splice(index, 1)cc.ArrayGetIndexOfValue(arr, value) --> arr.indexOf(value)cc.ArrayAppendObject(arr, addObj) --> arr.push(addObj)cc.ArrayAppendObjectToIndex(arr, addObj, index) --> arr.splice(index, 0, addObj)cc.ArrayGetIndexOfObject(arr, findObj) --> arr.indexOf(findObj)cc.ArrayContainsObject(arr, findObj) --> arr.indexOf(findObj) != -1// 修改大写函数为小写函数以符合命名规范cc.PRIORITY_SYSTEM --> cc.Scheduler.PRIORITY_SYSTEMcc.SWAP --> cc.swap // [Alpha 2新添加]cc.RANDOM_MINUS1_1 --> cc.randomMinus1To1 // [Alpha 2新添加]cc.RANDOM_0_1 --> cc.random0To1 // [Alpha 2新添加]cc.DEGREES_TO_RADIANS --> cc.degreesToRadians // [Alpha 2新添加]cc.RADIANS_TO_DEGREES --> cc.radiansToDegress // [Alpha 2新添加]cc.NODE_DRAW_SETUP --> cc.nodeDrawSetup // [Alpha 2新添加]cc.ENABLE_DEFAULT_GL_STATES --> cc.enableDefaultGLStates // [Alpha 2新添加]cc.DISABLE_DEFAULT_GL_STATES --> cc.disableDefaultGLStates // [Alpha 2新添加]cc.INCREMENT_GL_DRAWS --> cc.incrementGLDraws // [Alpha 2新添加]cc.CONTENT_SCALE_FACTOR --> cc.contentScaleFactor // [Alpha 2新添加]cc.POINT_POINTS_TO_PIXELS --> cc.pointPointsToPixels // [Alpha 2新添加]cc.SIZE_POINTS_TO_PIXELS --> cc.sizePointsToPixels // [Alpha 2新添加]cc.SIZE_PIXELS_TO_POINTS --> cc.sizePixelsToPoints // [Alpha 2新添加]cc._SIZE_PIXELS_TO_POINTS_OUT --> cc._sizePixelsToPointsOut // [Alpha 2新添加]cc.POINT_PIXELS_TO_POINTS --> cc.pointPixelsToPoints // [Alpha 2新添加]cc._POINT_PIXELS_TO_POINTS_OUT --> cc._pointPixelsToPointsOut // [Alpha 2新添加]cc.RECT_PIXELS_TO_POINTS --> cc.rectPixelsToPoints // [Alpha 2新添加]cc.RECT_POINTS_TO_PIXELS --> cc.rectPointsToPixels // [Alpha 2新添加]cc.CHECK_GL_ERROR_DEBUG --> cc.checkGLErrorDebug // [Alpha 2新添加]cc.CardinalSplineAt --> cc.cardinalSplineAt **[Alpha 2新添加]**// 常量cc.SPRITE_INDEX_NOT_INITIALIZED --> cc.Sprite.INDEX_NOT_INITIALIZED // [Alpha 2新添加]cc.DIRECTOR_PROJECTION_2D --> cc.Director.PROJECTION_2D // [Alpha 2新添加]cc.DIRECTOR_PROJECTION_3D --> cc.Director.PROJECTION_3D // [Alpha 2新添加]cc.DIRECTOR_PROJECTION_CUSTOM --> cc.Director.PROJECTION_CUSTOM // [Alpha 2新添加]cc.DIRECTOR_PROJECTION_DEFAULT --> cc.Director.PROJECTION_DEFAULT // [Alpha 2新添加]cc.TEXTURE_2D_PIXEL_FORMAT_RGBA8888 --> cc.Texture2D.PIXEL_FORMAT_RGBA8888cc.TEXTURE_2D_PIXEL_FORMAT_RGB888 --> cc.Texture2D.PIXEL_FORMAT_RGB888cc.TEXTURE_2D_PIXEL_FORMAT_RGB565 --> cc.Texture2D.PIXEL_FORMAT_RGB565cc.TEXTURE_2D_PIXEL_FORMAT_A8 --> cc.Texture2D.PIXEL_FORMAT_A8cc.TEXTURE_2D_PIXEL_FORMAT_I8 --> cc.Texture2D.PIXEL_FORMAT_I8cc.TEXTURE_2D_PIXEL_FORMAT_AI88 --> cc.Texture2D.PIXEL_FORMAT_AI88cc.TEXTURE_2D_PIXEL_FORMAT_RGBA4444 --> cc.Texture2D.PIXEL_FORMAT_RGBA4444cc.TEXTURE_2D_PIXEL_FORMAT_RGB5A1 --> cc.Texture2D.PIXEL_FORMAT_RGB5A1cc.TEXTURE_2D_PIXEL_FORMAT_PVRTC4 --> cc.Texture2D.PIXEL_FORMAT_PVRTC4cc.TEXTURE_2D_PIXEL_FORMAT_PVRTC2 --> cc.Texture2D.PIXEL_FORMAT_PVRTC2cc.TEXTURE_2D_PIXEL_FORMAT_DEFAULT --> cc.Texture2D.PIXEL_FORMAT_DEFAULTcc.Texture2D.setDefaultAlphaPixelFormat(format) --> cc.Texture2D.defaultPixelFormat = formatcc.Texture2D.getDefaultAlphaPixelFormat() --> cc.Texture2D.defaultPixelFormatcc.Texture2D.defaultAlphaPixelFormat() --> cc.Texture2D.defaultPixelFormatcc.dumpConfig --> cc.sys.dump// 公有转为私有cc.setup --> cc._setupcc.initDebugSetting --> cc._initDebugSettingcc.canvas --> cc._canvascc.drawingUtil --> cc._drawingUtilcc.renderContext --> cc._renderContextcc.gameDiv --> cc._gameDivcc.setContextMenuEnable --> cc._setContextMenuEnablecc.renderContextType --> cc._renderTypecc.CANVAS --> cc._RENDER_TYPE_CANVAScc.WEBGL --> cc._RENDER_TYPE_CANVAScc.mainRenderContextBackup --> cc._mainRenderContextBackupcc.RectFromString --> cc.spriteFrameCache._rectFromStringcc.PointFromString --> cc.spriteFrameCache._pointFromStringcc.SizeFromString --> cc.spriteFrameCache._sizeFromStringccs.CocoStudioVersion --> ccs.cocostudioVersion// 修正拼写错误ccs.DecotativeDisplay --> ccs.DecorativeDisplay 11.10 [Alpha 2新添加] Alpha 2中其他API改动 pauseSchedulerAndActions --> pauseresumeSchedulerAndActions --> resume cc.MoveBy.create(duration, x, y);cc.MoveBy.create(duration, cc.p(x, y)); getCString --> getStringsetCString --> setString ccs.comAttribute中重命名的函数 : cc.CallFunc的 initWithTarget 函数被重命名为 initWithFunction cc.MoveBy的 create 函数现在支持 x 和 y 分开作为位置参数 cc.Node中重命名的函数 :
12.[Beta新添加]Actions API变动 12.1 提供action的短方法创建方式
我们还针对action的相关类,增加了更加简单的创建方法,通过类名第一个字母改为小写就能创建出一个新的对象:
比如: var action = cc.MoveBy.create(2, cc.p(10, 10))
这个action可以通过下面更简单的方式创建: var action = cc.moveBy(2,cc.p(10,10)) 12.2 重新设计ease actions
所有的ease action其实是修饰性的action, 他们无法脱离目标action独立使用. 其有效部分也只是update函数,所以我们可以添加一个 easing 到 cc.ActionInterval 中, 它可以接受不同的ease对象来实现不同的ease动作效果。
新旧使用方法的比较,新的调用方式采用链式的调用更加简单、易用:
旧的调用方式: var easeMoveBy = cc.EaseIn.create(cc.MoveBy.create(2, cc.p(100,50)),0.3);
新的调用方式: var easeMoveBy = cc.moveBy(2,cc.p(100,50)).easing(cc.easeIn(0.3); 12.3 关于 cc.Repeat, cc.RepeatForever, cc.Speed 的新设计
以下的 cc.Repeat, cc.RepeatForever, cc.Speed 都是修饰性的actions, 所以我们添加对应的函数 repeat , repeatForever , speed , getSpeed , setSpeed 到 cc.ActionInterval 中. 通过这邪恶函数,开发者可以将原来复杂的动作以清晰的方式进行表示。All these changes allow developers to write complex actions more clearly.
旧的调用方式: var anAction = cc.Sequence.create( cc.Speed.create(cc.Repeat.create(cc.EaseIn.create(cc.MoveBy.create(2, cc.p(100,50)),0.3), 5),1.7), cc.RepeatForever.create(cc.RotateBy.create(2, 30)));
新的调用方式: var anAction = cc.sequence( cc.moveBy(2,cc.p(100,50)).easing(cc.easeIn(0.3)).repeat(5).speed(1.7), cc.rotateBy(2,30).repeatForever());
注意 : 所有的Actions的旧API都保留,并向前兼容。 12.4 新增Actions API列表
旧的调用方法 新的调用方法 cc.Repeat.create(action, num) action.repeat(num)
cc.RepeatForever.create(action) action.repeatForever()
cc.Speed.create(action, speed) action.speed(speed)
cc.Speed.setSpeed(speed) action.setSpeed(speed)
cc.Speed.getSpeed() action.getSpeed()
cc.EaseIn.create(action, rate) action.easing(cc.easeIn(rate))
cc.EaseOut.create(action, rate) action.easing(cc.easeOut(rate))
cc.EaseInOut.create(action, rate) action.easing(cc.easeInOut(rate))
cc.EaseExponentialIn.create(action) action.easing(cc.easeExponentialIn())
cc.EaseExponentialOut.create(action) action.easing(cc.easeExponentialOut())
cc.EaseExponentialInOut.create(action) action.easing(cc.easeExponentialInOut())
cc.EaseSineIn.create(action) action.easing(cc.easeSineIn())
cc.EaseSineOut.create(action) action.easing(cc.easeSineOut())
cc.EaseSineInOut.create(action) action.easing(cc.easeSineInOut())
cc.EaseElasticIn.create(action) action.easing(cc.easeElasticIn())
cc.EaseElasticOut.create(action) action.easing(cc.easeElasticOut())
cc.EaseElasticInOut.create(action, rate) action.easing(cc.easeElasticInOut(rate))
cc.EaseBounceIn.create(action) action.easing(cc.easeBounceIn())
cc.EaseBounceOut.create(action) action.easing(cc.easeBounceOut())
cc.EaseBounceInOut.create(action) action.easing(cc.easeBounceInOut())
cc.EaseBackIn.create(action) action.easing(cc.easeBackIn())
cc.EaseBackOut.create(action)
cc.EaseBackInOut.create(action)
action.easing(cc.easeBackOut())
action.easing(cc.easeBackInOut())
13.[Beta新变动]修改setText,getText为统一的API SetString, getString ccui.Text : setText --> setStringgetStringValue --> getString ccui.TextAtlas : getStringValue ==> getString ccui.TextBMFont : setText --> setStringgetStringValue --> getString ccui.TextField : setText --> setStringgetStringValue --> getString cc.EditBox : setText --> setStringgetText --> getString
其他详细文档列表: cc.log cc.spriteFrameCache cc.FileUtils 如何在JSB项目中使用extension more
软件开发
2015-05-18 18:03:00
Unity中制作游戏的快照游戏支持玩家拍快照
有些游戏支持玩家“拍快照”,也就是将游戏的精彩瞬间以图片的形式记录下来的功能。这个功能比较有趣,而且以后的用途也会很广,为此本节打算介绍:截取矩形区域内游戏视图,并将其显示在视图其它区域的方法。具体的操作步骤如下本文选自 Unity游戏开发技巧集锦 :
( 1 )在 Project 视图里,创建一个 C# 脚本文件,并命名为 ScreenTexture 。在此脚本中编写如下的代码: 01 using UnityEngine; 02 using System.Collections; 03 04 public class ScreenTexture : MonoBehaviour 05 { 06 // 公有成员 07 public int photoWidth = 50; // 矩形的宽度 08 public int photoHeight = 50; // 矩形的高度 09 public int thumbProportion = 25; // 截图的显示比例 10 public Color borderColor = Color.white; // 矩形框架的颜色 11 public int borderWidth = 2; // 矩形框的宽度 12 // 私有成员 13 private Texture2D texture; 14 private Texture2D border; 15 private int screenWidth; 16 private int screenHeight; 17 private int frameWidth; 18 private int frameHeight; 19 private bool shoot = false; 20 // 脚本初始化时,调用此函数 21 void Start () 22 { 23 screenWidth = Screen.width; 24 screenHeight = Screen.height; 25 frameWidth = Mathf.RoundToInt(screenWidth * photoWidth * 0.01f); 26 frameHeight = Mathf.RoundToInt(screenHeight * photoHeight * 0.01f); 27 texture = new Texture2D (frameWidth,frameHeight,TextureFormat.RGB24,false); 28 border = new Texture2D (1,1,TextureFormat.ARGB32, false); 29 border.SetPixel(0,0,borderColor); 30 border.Apply(); 31 } 32 // 运行游戏时,每帧都调用此函数 33 void Update () 34 { 35 // 鼠标左键按下的时候 36 if (Input.GetKeyUp(KeyCode.Mouse0)) 37 StartCoroutine(CaptureScreen()); 38 } 39 // 在 Game 视图上,绘制纹理 40 void OnGUI () 41 { 42 // 绘制矩形框的四个边 43 GUI.DrawTexture( 44 new Rect( 45 (screenWidth*0.5f)-(frameWidth*0.5f) - borderWidth*2, 46 ((screenHeight*0.5f)-(frameHeight*0.5f)) - borderWidth, 47 frameWidth + borderWidth*2, 48 borderWidth), 49 border,ScaleMode.StretchToFill); 50 GUI.DrawTexture( 51 new Rect( 52 (screenWidth*0.5f)-(frameWidth*0.5f) - borderWidth*2, 53 (screenHeight*0.5f)+(frameHeight*0.5f), 54 frameWidth + borderWidth*2, 55 borderWidth), 56 border,ScaleMode.StretchToFill); 57 GUI.DrawTexture( 58 new Rect( 59 (screenWidth*0.5f)-(frameWidth*0.5f)- borderWidth*2, 60 (screenHeight*0.5f)-(frameHeight*0.5f), 61 borderWidth, 62 frameHeight), 63 border,ScaleMode.StretchToFill); 64 GUI.DrawTexture( 65 new Rect( 66 (screenWidth*0.5f)+(frameWidth*0.5f), 67 (screenHeight*0.5f)-(frameHeight*0.5f), 68 borderWidth, 69 frameHeight), 70 border,ScaleMode.StretchToFill); 71 // 绘制矩形框中截取到的 Game 视图 72 if(shoot) 73 { 74 GUI.DrawTexture( 75 new Rect ( 76 10, 77 10, 78 frameWidth*thumbProportion*0.01f, 79 frameHeight*thumbProportion* 0.01f), 80 texture,ScaleMode.StretchToFill); 81 } 82 } 83 // 截取矩形框里的 Game 视图 84 IEnumerator CaptureScreen () 85 { 86 yield return new WaitForEndOfFrame(); 87 texture.ReadPixels( 88 new Rect( 89 (screenWidth*0.5f)-(frameWidth*0.5f), 90 (screenHeight*0.5f)-(frameHeight*0.5f), 91 frameWidth, 92 frameHeight), 93 0,0); 94 texture.Apply(); 95 shoot = true; 96 } 97 }
脚本代码中, 40 行的 OnGUI() 函数,使用 GUI.DrawTexture() 绘制了矩形框,以及矩形框中截取到的游戏视图; 84 行的 CaptureScreen() 函数,使用 texture.ReadPixels() 读取矩形框中的所有像素点,然后存储到 texture 中。触发“拍照”功能的操作是,在 Game 视图中的任意位置,单击鼠标左键,即 36 行代码实现的功能。
( 2 )将脚本 ScreenTexture 赋予 Main Camera ,然后选中 Main Camera ,在 Inspector 视图里可以设置脚本组件的一些属性,如图 2-19 所示 本文选自 Unity游戏开发技巧集锦 。
图 2-19 Main Camera 上 ScreenTexture 脚本组件的各属性 图 2-20 当前的 Scene 视图
( 3 )为游戏的场景添加一些几何体,并调整它们各自的位置,如图 2-20 所示。
( 4 )运行游戏,当在 Game 视图中的任意位置,单击鼠标左键的时候,可在视图的左上角看到矩形框中截取到的游戏视图,如图 2-21 所示 本文选自 Unity游戏开发技巧集锦 。
图 2-21 运行游戏,查看截图的效果
软件开发
2015-04-17 10:18:00
[root @localhost petrify]# ./configure
bash: ./configure: 没有那个文件或目录 没有./configure 是不支持C编译,装机的时候,没有装C语言 则安装以下rpm包 yum -y install ntp vim- enhanced gcc gcc-c++ gcc-g77 flex bison autoconf automake bzip2-devel \ ncurses-devel openssl-devel libtool* zlib-devel libxml2-devel libjpeg-devel libpng-devel libtiff-devel \ fontconfig-devel freetype-devel libXpm-devel gettext-devel curl curl-devel pam-devel \ e2fsprogs-devel krb5-devel libidn libidn-devel


当然也不全是这样的,像mysqli中....
http://blog.chinaunix.net/u3/117528/showart_2287937.html
软件开发
2015-04-02 18:31:00
原理介绍:使用nstd工具进行进程崩溃时内存和堆栈转储。 编译Release版本时打开调试选项,将exe和pdb文件一起发布。
2.使用批处理命令设置Windows系统在进程崩溃时调用的调试器为ntsd @reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug" /v "Auto" /t REG_SZ /d "1" /f
该批处理指令,设置AeDebug调用前是否需要弹消息框确认?默认为0,设置1表示自动处理不弹确认框。如果大家安装过Visual Studio,可能都遇到过下列弹框:

这就是VS设置了AeDebug调试器后,遇到进程崩溃,然后弹框确认是否要附加到该进程进行调试的过程。
然后设置ntsd的调试转储指令:
@reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug" /v "Debugger" /t REG_SZ /d "D:\Dump\ntsd.exe -p %%ld -e %%ld -g -c \".dump /ma /u D:\Dump\dump.dmp; .logopen /t D:\Dump\dump.txt; .time; .echo Process Status:; ^|; .echo Thread Status:; ^~; .echo Stack Status:; kpn; .logclose; q\"" /f
我把的ntsd程序拷贝到d:\dump目录。
-p %%ld 传入进程ID,-c 执行dump指令:echo 打印线程信息,线程堆栈,最后再退出。
批处理安装脚本和ntsd程序我都已经打好包了,可以到 这里下载 。
3.下面通过一个实例来演示下效果: #include void test2() { int a = 1; int b = 0; int c = a/b; } void test1() { test2(); } int main(int argc, char** argv) { test1(); return 0; }
通过除0错误来构造一次崩溃,test1和test2是为了演示调用堆栈。
通过本方法抓取的堆栈文本如下: Opened log file 'D:\Dump\dump_22d4_2014-09-30_15-15-33-062.txt' Debug session time: Tue Sep 30 15:15:33.063 2014 (GMT+8) System Uptime: 2 days 3:35:54.545 Process Uptime: 0 days 0:00:00.923 Kernel time: 0 days 0:00:00.015 User time: 0 days 0:00:00.000 Process Status: . 0 id: 3854 attach name: D:\test\CoreDump\DumpExampleNormalStack.exe Thread Status: . 0 Id: 3854.3138 Suspend: 1 Teb: 7ffdf000 Unfrozen Stack Status: *** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Windows\SYSTEM32\ntdll.dll - # ChildEBP RetAddr 00 002dfb2c 01321038 DumpExampleNormalStack!test2(void)+0x18 01 002dfb34 01321048 DumpExampleNormalStack!test1(void)+0x8 02 002dfb3c 01321159 DumpExampleNormalStack!main(int argc = 1, char ** argv = 0x003ea488)+0x8 *** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Windows\system32\kernel32.dll - 03 002dfb84 76e2ee1c DumpExampleNormalStack!__tmainCRTStartup(void)+0xfe WARNING: Stack unwind information not available. Following frames may be wrong. 04 002dfb90 77ba37eb kernel32!BaseThreadInitThunk+0x12 05 002dfbd0 77ba37be ntdll!RtlInitializeExceptionChain+0xef 06 002dfbe8 00000000 ntdll!RtlInitializeExceptionChain+0xc2 Closing open log file D:\Dump\dump_22d4_2014-09-30_15-15-33-062.txt
切记:在Release版本中需要把调试选项打开,而且生成的pdb文件和exe要放在同一目录下。
批处理安装脚本和ntsd程序我都已经打好包了,可以到 这里下载 。
如果是查找内存泄漏问题,可以参考本人另一篇 博文 。如果需要用谷歌查询技术问题,可以使用 这里 。
软件开发
2015-04-02 11:22:03
xs x2 xe x1 x3 x1
软件开发
2015-03-15 17:51:00
unity中 一般都用coroutine(协同程序,协程)实现定时任务,自己写Timer and Invoke 也可以实现,主要分析unity中的coroutine的实现原理,
coroutine 比如WaitForSeconds 内部都是迭代后,添加到CallDelay的 中 给DelayedCallManager管理,内部存储结构是一个multiset(讲道理为什么不用map),该定时器的触发条件是CallBack的时间到了,通过长时间线(时间戳)来处理,而不是每一帧Tick一次,排序值就是CallBack的time,这种非顺序容器(物理排列)的一个好处就是在遍历的时候可以动态删除添加,而不影响整体排序值,删除和添加代价都在很小范围内,而不会像vector那样数据前移,基于稳定排序后,在multiset中的排序值始终是按照添加顺序排序(同一个时间戳情况下)。如果排序插入的结果不是稳定 的那么会出BUG,coroutine的调用顺序 一定情况下不会按照StartCoroutine的顺序执行。
即StartCoroutine来做定时任务 并没有性能优势。更多的是写法上的优雅,因此不建议大量使用
TestCode

内部再对WaitForSeconds 添加到定时器任务,达到协同程序的目的

发起添加一个定时器CallBack,coroutine内部IEnumerator后 也是发起calldelayed
如果是0.0f的话,当前Update不处理,下一次Update才处理time=-1f
calback
update
coroutine内部获得 IEnumerator后的处理
软件开发
2017-09-28 18:50:00
从零开始开发一款H5小游戏(一) 重温canvas的基础用法
https://segmentfault.com/a/1190000005892330
canvas自适应屏幕大小
http://www.cnblogs.com/mihoutaoguniang/p/5998511.html
[Canvas前端游戏开发]——FlappyBird详解
http://web.jobbole.com/84771/
用Phaser来制作一个html5游戏——flappy bird (一)
软件开发
2017-09-07 16:29:00
UGUI的优点新UI系统四 开源
新UI系统是开源的,所以开发者可以看到新UI系统实现的源码,并加以修改和使用。
开源授权协议——MIT/X11
Unity所搭载的新UI系统,是在开源授权协议MIT/X11之下被公开的!所以开发者可以查看、修改其中的源码,来为己所用!提示:MIT/X11开源授权协议的查看网址是:http://opensource.org/licenses/MIT。
源代码托管网站——BitBucket
新UI系统的源代码被托管在BitBucket上,读者可以从此网站上下载源码,网址如下,打开的页面所示。https://bitbucket.org/Unity-Technologies/ui

单击页面左侧列表中的Downloads选项以后,进入的页面,单击Download repository链接即可开始源代码的下载,下载到的源码压缩包,及其中的文件和文件夹。
查看源代码项目
读者可以使用Visual Studio(微软提供的代码编辑器)或者MonoDevelop(Unity自带的代码编辑器),打开新UI系统源代码项目。以MonoDevelop为例,选中从源码压缩包中解压出来的文件UISystem.sln,右击鼠标从打开方式中选择MonoDevelop。
在MonoDevelop中,被打开的项目结构如图1-11所示。此项目中就包含了新UI系统的所有源代码,开发者可以任意查看和修改。
提示:有关此源代码项目的更多介绍,读者可以查看源代码压缩包解压出来的文件README.md。此文件是以md为后缀的文本文件,在Windows平台上,推荐使用MarkdownPad对其进行查看和编辑(使用免费的版本),其官方软件的下载网址如下,页面如图1-12所示。另外,主页上也显示了此软件打开md文件的效果图。
http://www.markdownpad.com/
图1-12 MarkdownPad编辑器官网主页
软件开发
2017-08-17 14:59:00
加密主要包含通信数据和存储数据加密,目的都是为了保证其传送和储存的隐秘性,从而保证数据的安全。目前常见的加密方式有对称加密、非对称加密、hash加密、hash加盐加密等,这些在游戏中都会用的,我们会对其用途以及缺陷一一说明,当然了,为了保证其加密算法的安全以及高效,我们也会介绍几种自定义的加密算法,看看加密如何来维护我们的数据安全。

1、对称加密
对称加密算法是应用较早的加密算法,技术成熟。主要就是对密钥的一个维护,发送方把数据和密钥通过一定的加密算法处理后,发送给接收方,接受方接到之后在使用相同密钥及算法的逆算法对密文进行解密。这就是一般的对称加密算法过程。常见的对称加密算法有AES、 DES , 3DES ,TDEA, Blowfish , RC5 , IDEA 等,建议用AES,速度快,安全性也可以。
对称加密算法的特点是算法公开、计算量小、加密速度快、加密效率高。缺点主要就是密钥需要双方都有,如果密钥被窃取,那么加密就会比第三方破解,特别是游戏中,密钥如果存放在客户端中,容易被破解反编译到。
我们可以采取登陆消息和逻辑消息采用不同的密钥,登陆验证通过之后,服务器为每个用户分配不同的密钥,然后把逻辑密钥传送给客户端,以此保证密钥的不确定性,从而增加游戏的安全。

2、非对称加密
非对称加密算法使用两把完全不同但又是完全匹配的一对钥匙— 公钥 和 私钥 。在使用不对称加密算法加密文件时,只有使用匹配的一对公钥和私钥,才能完成对明文的加密和解密过程。这对于对称加密算法来说,又安全了一步,也是目前https常用的加密方式,公钥可以分配和暴露给所有想要访问的请求者,但密钥一定牢牢的掌握在服务器这边,如此对通信来说,安全性有保证。常用的加密算法,RSA,DSA,ECC。
非对称加密算法,优点就是安全,但缺点就是不够快,比较耗费cpu,如果在游戏中每一次消息都有其加密,对cpu的损耗还是挺高的,所以游戏中一般不用这种加密方式,当然了也看游戏类型,如果对这方面的性能要求不高,安全性要求有很高,采用业务科厚非(那个游戏这么傻啊)。

3、hash加密
hash加密,就是常见的使用MD5、SHA1等单向HASH算法保护密码,使用这些算法后,无法通过计算还原出原始密码,而且实现比较简单也高效,因此很多互联网公司都采用这种方式保存用户密码。
但安全性越来越担忧了,因为随着彩虹表技术的兴起,可以建立彩虹表进行查表破解,目前这种方式已经很不安全了。

4、hash 加盐加密
hash加密既然容易被彩虹表破解,那么可以采用加盐、多次HASH等扩展,这样可以在一定程度上增加破解难度。常见的方式也是发送方和接受方,维护一个盐池,加密和解密的时候加上这一段盐池来进行hash。
不过这种算法又回到了对称加密中对密钥的保护问题了,如果盐池泄露,别人依然会破解。
怎么办?有人又想出了,让盐池随机的方式,比如PBKDF2算法,原理大致相当于在HASH算法基础上增加随机盐,并进行多次HASH运算,随机盐使得彩虹表的建表难度大幅增加,而多次HASH也使得建表和破解的难度都大幅增加。一次密码验证过程进行1000次HASH运算,对服务器来说可能只需要1ms,但对于破解者来说计算成本增加了1000倍,而至少8字节随机盐,更是把建表难度提升了N个数量级,使得大批量的破解密码几乎不可行,该算法也是美国国家标准与技术研究院推荐使用的算法。

5、自定义加密
终于到这个了,以上那么多高大上的加密算法,都是业界比较成熟的算法,好处是处处有API支持也有人实现,拿来就用,坏处也是,算法格式规整透明,除了非对称算法,都有其对应的破解方式。游戏的加密要怎样?安全、安全,高效、高效,你不能一个加密算法就耗费我100ms的cpu吧,太浪费了。
我们可以尝试一种动态加密的方式,就是每一次请求保证用不同的密钥,这样即便一个消息被截取破解了,下一次密钥又不一样,如此破解者会比较崩溃。怎么做?我简单说下思想。 每个消息必须有唯一id,一个是防止消息重放,一个可以用来做我们的加密。比如我们初始的时候有一个密码池 A=【1,2,3,4,5】,每次消息从服务器发送出去的时候,消息ID都+1,
当前密钥=A.index(消息ID%A.length),如此就能保证每次密钥不一样,具体拿到密钥如何加密,完全可以用自己的加密方式,比如把二进制一部分截取通过密钥移位操作,或者算法运算都可以。
我们的目的一个保证密钥动态,一个就是保证算法足够高效。
算法 特点 有效破解方式 破解难度 其它 算法名字
对称加密 可以解密出明文 获取密钥 需要确保密钥不泄露 AES,3DES
非对称加密
HASH加密 加盐HASH Pbkdf2 自定义算法
一对钥匙— 公钥 和 私钥
简单,高效 对hash加盐处理 对hash的盐池进行随机处理 非标准,高效,安全
获取私钥
碰撞、彩虹表 碰撞、彩虹表

效率低,安全性高

需要确保“盐”不泄露 随机盐池不能太大,8个字节比较合适 算法的灵活性
RSA,DSA,ECC,DH
MD5,SHA1,SHA1256 同上 同上 动态密钥


-----------------------------------------------------------------------------
想看更多有趣原创的技术文章,扫描关注公众号。
关注个人成长和游戏研发,推动国内游戏社区的成长与进步。
软件开发
2017-08-09 08:29:00
此类工具搜索过一些,都不要好用,于是决定自己实现一个。
大图集拆开为小图片,由于是工具,于此效率不太重要,直接用Cocos2dx随手写了一个。
基本思想是直接读取,然后把该SpriteFrame输出为文件即可 #include "HelloWorldScene.h" #include "cocostudio/CocoStudio.h" #include "ui/CocosGUI.h" #include #include #include #include #include #include #include USING_NS_CC; using namespace std; using namespace cocostudio::timeline; Scene* HelloWorld::createScene() { // 'scene' is an autorelease object auto scene = Scene::create(); // 'layer' is an autorelease object auto layer = HelloWorld::create(); // add layer as a child to scene scene->addChild(layer); // return the scene return scene; } // on "init" you need to initialize your instance bool HelloWorld::init() { // 1. super init first if (!Layer::init()) { return false; } vector files; const char *TEM_WORK_FILE_NAME = "filelist.txt"; string path = "res"; string cmd_txt = "cmd.exe /c DIR " + path + " /S /B > "; cmd_txt += TEM_WORK_FILE_NAME; system(cmd_txt.c_str()); fstream f; f.open(TEM_WORK_FILE_NAME, ios::in); vector _queue; string buf; while (getline(f, buf)) { // cout << buf << " ... " << endl; // cout<WriteToFile(); f.close(); //FileUtils::getInstance()->removeFile(TEM_WORK_FILE_NAME); //system("pause"); //return 0; //开始转换 // vector files; // //files.push_back("game_1_1.plist"); //files.push_back("game_1_2.plist"); //files.push_back("game_1_3.plist"); /*files.clear(); files.push_back("ui/awake.plist"); files.push_back("ui/yingxiongfuben_1.plist"); files.push_back("ui/yuanbaobiaoche.plist"); files.push_back("ui/xingkong2.plist"); files.push_back("ui/synthesis_equipment_4.plist"); files.push_back("ui/updateRecommend3.plist"); files.push_back("ui/shenmishangcheng.plist"); */ for (auto & name : files) { string dir = name.substr(0, name.size() - strlen(".plist")); //log((FileUtils::getInstance()->getWritablePath() + "/" + dir).c_str()); FileUtils::getInstance()->removeDirectory(FileUtils::getInstance()->getWritablePath() + "/" + dir); FileUtils::getInstance()->createDirectory(FileUtils::getInstance()->getWritablePath() + "/" + dir); SpriteFrameCache::getInstance()->addSpriteFramesWithFile(name); auto mapp = SpriteFrameCache::getInstance()->_spriteFrames; for (auto & cp : mapp) { auto sp = Sprite::createWithSpriteFrameName(cp.first.c_str());// Sprite::createWithSpriteFrameName("accomplishTitle.png"); sp->setPosition(sp->getTextureRect().size.width / 2, sp->getTextureRect().size.height / 2 ); this->addChild(sp); if (sp->getTextureRect().size.width <= 0 || sp->getTextureRect().size.height <= 0) {//invalid sprite continue;; } log((" out:" + dir + "/" + cp.first).c_str()); auto re = RenderTexture::create(sp->getTextureRect().size.width, sp->getTextureRect().size.height); re->begin(); sp->visit(Director::getInstance()->getRenderer(), Director::getInstance()->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_TEXTURE), 0); re->end(); string path = dir + "/" + cp.first; re->saveToFile(path); } SpriteFrameCache::getInstance()->removeSpriteFramesFromFile(name); TextureCache::getInstance()->removeAllTextures(); SpriteFrameCache::getInstance()->removeUnusedSpriteFrames(); } //Director::getInstance()->end(); return true; }
软件开发
2017-07-25 20:38:00
通过公式改变材质Color属性,实现黑白图片效果。 Shader "Unlit/NewUnlitShader" { Properties { _MainTex ("Texture", 2D) = "white" {} _GrayImage ("ChangeGray",float) = 1 } SubShader { Tags { "RenderType"="Opaque" } LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; half4 color : COLOR; float2 texcoord : TEXCOORD0; }; struct v2f { float2 texcoord : TEXCOORD0; half4 color : COLOR; float4 vertex : SV_POSITION; }; sampler2D _MainTex; float _GrayImage; float4 _MainTex_ST; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.color = v.color; o.texcoord = v.texcoord; return o; } fixed4 frag (v2f i) : COLOR { fixed4 col; if (_GrayImage == 1) { col = tex2D(_MainTex, i.texcoord); float grey = dot(col.rgb, float3(0.299, 0.587, 0.114)); col.rgb = float3(grey, grey, grey); } else { col = tex2D(_MainTex, i.texcoord) * i.color; } return col; } ENDCG } } }
软件开发
2017-06-18 21:40:00
#####一、步骤 1.新建一个场景(命名为:005_textTween) 2.创建一个text 3.创建一个脚本(myText) 4.之前对游戏物体做一个动画 是通过Rect Transform 5.使用Dotween里面的一个方法DOText进行一个文本的动画 (如果原来的text有文字,在动画过程中把原有的文字替换掉)
#####二、code using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; // 引用UI的命名空间 using DG.Tweening; public class myText : MonoBehaviour { private Text text; //文本 // Use this for initialization void Start () { text = this.GetComponent (); // 直接获取游戏物体 text.DOText("DOText是用来动画显示text的,接下来,开始动画了,效果是一个字一个字显示的",4,);//richtextEnabled 第三个参数是一个富文本 } // Update is called once per frame void Update () { } }
软件开发
2017-04-26 14:54:00
A tween's life When you create a tween it will play automatically (unless you change the global defaultAutoPlay behaviour) until it completes all its loops. When a tween is complete it is automatically killed (unless you change the global defaultAutoKill behaviour), which means you won't be able to use it anymore. If you want to reuse the same tween, just set its autoKill behaviour to FALSE (either by changing the global autoKill settings for all tweens, or by chaining SetAutoKill(false) to your tween). If your tween's target becomes NULL while a tween is playing errors might happen. You'll have to either be careful or activate the safe mode
#####一、补间的生活
######当您创建补间时,它将自动播放(除非您更改全局 defaultAutoPlay 行为),直到它完成所有循环。 ######当补间完成时,它会自动被杀死(除非您更改全局 defaultAutoKill 为),这意味着您将无法再使用它。 ######如果要重复使用相同的补间,只需将其autoKill行为设置为FALSE(通过更改所有tweens 的 全局autoKill 设置,或将 SetAutoKill(false) 链接到补间)。 ######如果补间的目标在补间的时候变为空,可能会发生错误。您必须小心或激活 安全模式
#####二、 动画的声明周期 Chained callbacks 常用--- OnComplete(TweenCallback callback) 设置一个回调,在tween到达完成时触发,所有循环都包含在内。 OnKill(TweenCallback callback) 设置一个回调,在tween被杀死的那一刻被触发。 OnPlay(TweenCallback callback) 设置回调,当补间设置为播放状态时,在任何最终延迟之后将被触发。每次补间从暂停状态恢复播放时也称为。 OnPause(TweenCallback callback) 设置回调,当补间状态从播放状态更改为暂停时将被触发。如果补间程序的autoKill设置为FALSE,则在补间程序完成时也会调用此函数。 OnRewind(TweenCallback callback) 设置一个回调,Rewind通过在向后播放时通过调用或达到开始位置来重新播放补间即将触发。 注意:重新卷绕已被重绕的补间不会触发此回调。 OnStart(TweenCallback callback) 设置一个回调,当补间开始时将被触发(这意味着当补间设置在第一次,在任何最终延迟之后被设置为播放状态时)。 OnStepComplete(TweenCallback callback) 设置一个回调,每次补间完成一个循环循环后将被触发。 OnUpdate(TweenCallback callback) 设置将在每次补间更新时触发的回调。 OnWaypointChange(TweenCallback callback)设置一个回调,当路径补间的当前航点更改时将被触发。 这是一个特殊的回调,与其他回调相反,需要接受一个类型的参数int(这将是新更改的航点索引)。
软件开发
2017-04-26 14:53:00
先说这本书的第8章,是讲矩阵的。很明显,这一章里说的矩阵,可以理解为一种运动过程。将一个向量与这个矩阵相乘时,就代表对这个向量执行这个运动过程。
然后跳到10.6节,看标题是【表达形式之间的转换】。明显是说“一种事物”有多种描述形式、表达形式,这一节呢,就是要根据其中一种描述形式,求出其他等价的描述形式。那么这里的“一种事物”是说啥呢?看它第一段的最后一句:本节将讨论怎样将角位移从一种形式转换到另一种形式。嗯哼,我很自然的认为“一种事物”指的就是角位移,也可以认为就是第8章说的变换矩阵。结果呢,这一节余下的内容,求的矩阵,并不是角位移,即不是第8章说的运动过程,而是:同一个点在不同坐标系下的坐标值之间的对应关系,即同一个点,在不同的坐标系下有不同的坐标值,这些不同的值描述的是同一个点,这个矩阵就是让你能够从一个值求出这另一个对应的值,而不是让你将一个点执行一个运动过程,求出这个点现在到了什么地方。这两种理解表达的内涵是完全不一样的,尽管可能从矩阵字面值来看,两种理解所对应的矩阵是个互逆关系而已。但造成的混乱十分明显,我觉得这是这本书最大的bug。这个bug也清楚的解释了为什么h、p、b前面都要加个负号,而不是其他文献中的h、p、b矩阵直接连乘。
所以,最后再强调一下,书里10.6求的那几个M矩阵,是表示同一个点的在不同坐标系下的不同表述之间的对应关系,不是表示将一个点在一个坐标系下执行运动过程。
软件开发
2017-04-18 18:33:00
https://www.codeproject.com/kb/game/autotiles_algorithm.aspx#_comments
这篇文章的js实现
演示和具体代码请看这里 http://jsdo.it/Acans/qRex
实际上个算法很简单,实现起来比rpg maker的模式简单很多
图1
图2
算法核心
地块区域如图2,由1~9个tile组成,需要在5号目标地方放入图块时,只需要改变红色区域的bit块为1 ,重新计算每个tile的二进制下标,对应显示图1的图块即可。 if (x > 0) { if (y > 0) tile[x - 1][y - 1] |= 0x8; // 1000 tile[x - 1][y] |= 0xA; // 1010 if (y < tile_width - 1) tile[x - 1][y + 1] |= 0x2; // 0010 } if (y > 0) tile[x][y - 1] |= 0xC; // 1100 tile[x][y] |= 0xF; // 1111 if (y < tile_height - 1) tile[x][y + 1] |= 0x3; // 0011 if (x < tile_width - 1) { if (y > 0) tile[x + 1][y - 1] |= 0x4; // 0100 tile[x + 1][y] |= 0x5; // 0101 if (y < tile_height - 1) tile[x + 1][y + 1] |= 0x1; // 0001 }
这个算法是根据tile计算的。
还有个算法可以根据顶点来计算
图3
需要在红色区域的中心放置图块时,只需要设置红色区域bit=1 ,重新计算下标对应到图1的图块即可。 if (x > 0) { if (y > 0) tile[x - 1][y - 1] |= 0x8; // 1000 if (y < tile_height) tile[x - 1][y] |= 0x2; // 0010 } if (x < tile_width) { if (y > 0) tile[x][y - 1] |= 0x4; // 0100 if (y < tile_height) tile[x][y] |= 0x1; // 0001 }

效果图
软件开发
2017-03-24 16:59:00
我平常自己写点小东西都是用的 GLM(OpenGL Mathematics)数学库。GLM 功能很强大,也很方便用,直接包含在项目中编译即可。但是文件太多,有时候想写个小例子只用到其中个别函数,而不得不把整个库的代码都拉过来,而且最重要的是我个人更喜欢 c ,很多时候因为用 GLM 不得不采用 cpp 语法。所以就从网上搜索了一下,找了三个 c 代码的数学库。
linmath
linmath 功能相对很多,我当时搜索时是在 stackoverflow 上找到的,作者在回答时推荐了自己的这个库,还说有奇怪的 bug ,所以我没有用,不过我蛮喜欢这个库中对于 c macro 的使用。
kazmath
kazmath 功能很全面,作者也是大名鼎鼎。看介绍说 Cocos2d 也参考了这个库。
math3d_h
math3d STB style 。单个头文件即可使用。我用的就是这个库。这个库代码超级简洁。而且包含了完整的单元测试。下面是简单的代码片段。这个文件中只包含了两个类型 vec3_t 和 mat4_t 。矩阵与 vector 相乘时有函数 m4_mul_pos 和 m4_mul_dir ,分别对应点坐标和矢量坐标,矢量坐标会忽略 w 分量而点坐标不会,具体可以查看实现。 mat4_t viewmat = m4_look_at(vec3((float)sin(t) * radius, 25.0f, (float)cos(t) * radius), vec3(0, -50.0f, 0), vec3(0, 1.0f, 0)); mat4_t persmat = m4_perspective(45.0f, (float)w / (float)h, 0.1f, 1000.0f);
软件开发
2017-03-19 23:06:00