近期评论

    今日最热

    No top posts yet

    RMMV 昼夜系统

    这个插件目前实现了随时间流逝的昼夜日照变化,以及基于Tile的固定光源。

    多说无益,直接上图:

    白昼

    正午

    黄昏

    夜晚

    清晨

     

    我想,明眼人应该已经看出来了,这里的日光变化是用类似Tint Screen的方式靠滤镜(Filter)实现的,而不是像TerraxLight那样在Spriteset_Map的上方绘制一层黑幕。事实上,本插件是在正常的ScreenToneChanger的下方增加了一层光照滤镜,Tint Screen的操作还是可以正常执行。我个人感觉这样的视觉效果会更好,当然见仁见智。

    Game Maker们可以用插件的参数来定义一天之中的时段,并设置每个时段的“起止时间”、“光照效果”、“灯光效果”和“Tileset替换功能”。具体如下:

    如上所见,时段的定义是一个列表,可以在其中创建任意多的项。发挥想象力吧!

    每一个时段的具体信息如下:

    “Start Time”和“End Time”应该不需要解释了。“Screen Tone”定义了该时段使用的光照滤镜,数值的意义和设置Tint Screen的时候完全一直,可以照抄过来。

    第四行可能让人有点费解,其实就是给光照滤镜的作用范围下一个定义。比如,灯火通明的室内显然不会受到外部日光变化的影响。

    第五行“Available lightings”是一个列表,定义了这个时段会起作用的灯光。其中每一项是一个Id,对应灯光设置中的一项,这个后面再说。

    第六行又有点让人费解了。先看看效果吧:

            

    看出区别了吗?就是窗户。

    室内虽然不容易受到日光的影响,但有些细节还是需要根据时段而变化。这个选项的作用就是定义一些Tileset对,让它们在进入特定的时段后会互换,从而产生时间变化的感觉。

    最后一行“Indicator”指定了一个Switch作为特定时段的标识,如果在该时段内则该Switch变成On,否则变成Off。也可以设为0,也就是不需要这个指示器,因为插件内部会保留自己的指示器。但是,如果你需要根据不同的时段触发不同的事件时,就有必要定义这个量。

     

     

    有了时段的定义之后,产生时间流逝的视觉效果的工作就完成了一大半了。下面需要考虑的就是灯光。

    本插件实现灯光效果的方式和TerraxLight的类似,都不是画上灯光,而是去改变“夜幕”。不过,因为本插件使用滤镜来实现光照变化,所以实现细节和TerraxLight不同。而且……现在只实现了基于Tile的静止圆形光源,功能上不如TerraxLight那么丰富。

    先看看效果:

    事实上,灯光的作用主要是取消特定范围内的滤镜效果,实现方式是改写PIXI的ColorMatrixFilter类,用新的FragmentShader来实现带掩码(Mask)的滤镜效果。

    所以,同样是白光,不同的强度(色彩的明度)会使取消滤镜的程度不同,如下(三个光源分别是街灯、井口和路中间的花):

    灯光的颜色也会影响滤镜的过滤结果,实际上可以认为是加上了色彩滤镜,如下:

    本插件和TerraxLight的目标略有不同,基本只是以烘托气氛为主,所以不容易得到恐怖游戏式的阴暗感。不过,如果把夜色加深或许也会有类似的效果吧,比如:

    见仁见智。

     

    好,说了这么多效果,下面来介绍灯光的设置。之前介绍时段的时候已经提到,时段当中能够设置生效的灯光列表,用来让灯光在不同时段产生不同的行为。

    下面是如何设置特定的灯光效果:

    如上所见,灯光效果也是一个列表,可以添加任意多的项。不过,因为现在插件使用的是Terrain Tag触发,而RMMV的Terrain Tag最多只能到7,所以也不可能太多。

     

    第一行“Terrain tag”是触发这个灯光效果的Terrain Tag(就是Tileset设置里面最下面的一个选项,平时很少用到)。只要在有效的时段内绘制具有这个Terrain Tag的Tile,就会自动在这个Tile上绘制相应的灯光效果。

    第二行和第三行是灯光的颜色和范围,因为现在只有圆形灯光,所以范围就是半径。色彩的值虽然有四个子项,但那是为了能复用四通道色彩结构,其实最后一项gray是无效的。

    最后一行“Blink”决定这个灯光是静态还是动态。一般电灯可以设为静态,而火光则基本是动态比较好。动态的灯光会每隔一秒重绘一次,产生微微晃动闪烁的感觉。

    虽然看上去好像一个Terrain Tag只能设置一种灯光效果,但其实也可以创建很多拥有相同Terrain Tag的灯光项,只要让它们在不同的时段生效就行了,还能产生同一组灯在不同时间效果不同的感觉。

     

    那么,本插件的主要功能就介绍完了……好像还忘了什么……

    对了,设置里面还有两项没介绍。就是这两个:

     

    这两项各代表一个变量(Variable),用来保存当前的时间。本插件只会读取这个时间,而永远不会去修改这两个值!也就是说,本插件并不负责控制游戏中时间的流逝,这个需要游戏制作者自己控制。最简单的方法是用一个并行Common Event按特定时间间隔改写这两个值。

    之所以使用这种方式,是因为我觉得这样比较灵活,也能减少插件的设置参数(绝对不是我做不出来时间流逝喔)。因为变量的值和时段的起止时间都没有限定,所以你完全可以让一天有200个小时,一个小时只有10分钟,而且也可以根据需要在不同地图使用不同的时间流逝速度。比如,在房间里和在大地图上行走同样的距离显然不可能用同样长的时间,对吧?

    好了,这样本插件的所有细节基本都介绍了。最后一点温馨提示:

    为了能做出带掩码的滤镜效果,所以直接改写PIXI的类和渲染代码,因此兼容性难以保证,请有兴趣的同学测试之后告诉我,谢谢。另外,因为这只是0.1版,肯定会有Bug,如果遇到问题可以在这里或者我的主页留言。

    下载:

    http://warmbloodedsnake.com/blog/wp-content/uploads/2018/01/WBS_daynight.zip

    这里面有三个文件,常用插件的人应该很容易分辨。

    js文件是插件主体,放在plugins目录下。

    两个png文件是对应的Tileset的夜间版本,可以拷贝到img/tilesets目录下,然后创建对应的夜间版Tileset,就可以测试。

     

    物理内存故障引发CPU死锁 Physical memory failure cause CPU lockup

    故障症状:

    大量读写内存的程序运行过程中,终端反复打印出“BUG: soft lockup – CPU#? stuck for ?s”警告信息。

    同时,ssh等远程终端反应变得非常迟缓,甚至拒绝相应。

    检查进程运行情况(使用top或者ps),发现有除我的程序之外,有数个系统进程同样出现大量占用CPU的情况,包括ksoftirqd、kthreadd、kondemand,flush-???,kdmflush等。

    有时,处于这种故障情况下的主机可以正常启动,在刚启动时也不会表现出任何症状。

    故障起因:

    这种症状在我的工作站上出现过多次,其中几次在重启时会不时出现对内存错误的警告信息。我没有对系统进行任何人为修改,包括任何模块和驱动程序,因此基本可以排除内核错误引发故障的可能。

    经过检查,我确信这些故障都源自物理内存错误。内存无法正常读写,造成部分指令在取指或者取值时无法正常执行,从而使CPU卡死,watchdog误认为发生了死锁。

    故障修复:

    我使用的方法相当简单粗暴,排除所有故障的内存条(有时因为得不到警告信息,所以必须使用排除法来确定实际故障的内存条,在双CPU和双通道内存的主机上,通常每次需要排除一对处于相对位置的内存条)。

    Symptoms:

    After running a memory consuming program for a relatively long time, I would get the warning “BUG: soft lockup – CPU#? stuck for ?s” repeatedly.

    Meantime, reaction of remote terminals such as ssh became extremely slow or even froze.

    After checking the status of processes, I discovered that a few system processes besides my program were taking a large amount of CPU time, including ksoftirqd, kthreadd, kondemand, flush-???, kdmflush.

    Sometimes, computers under this condition could boot normally, and upon booting no sympton would show immediately.

    Cause:

    These symptoms showed on my workstation many times, some of which were accompanied with warnings of memory failure on booting. I had not changed any of the modules or drivers of the system, so it is highly unlikely that these symptoms were caused by actural kernel errors.

    I believe these problems originated from physical memory failure. When a memory unit can not be read/written properly, instructions may be stuck when fetching command or data. This could cause CPU to be stuck too and make watchdog mistake it for a soft lockup.

    Solution:

    My solution is perfactly simple: just remove the broken memory card(s) (when there was no warning information, you might need to remove the cards one by one to determine which one is broken.)

    S60中使用CS结构实现事件(消息)驱动的简单方法

    C/S结构的进程间通信机制可谓Symbian平台的一大特色,使用此结构相应的一系列API可以方便地实现安全的进程间通信。很多时候,Client和Server的互动需要使用事件(消息)驱动的方式来进行,事实上,很多系统Server如Message Server等都提供此类的功能。在自己编写的程序中如何实现这种功能的问题困扰了我一段时间,因为本身支持C/S结构的API(RSessionBase等)并不支持这样的工作方式(还是实际上支持而我不知道?当然我也不敢自认专家),最后我采用了一种自创(不一定新颖)的方法来实现了这个功能。

    一、定义一个接口类如MObserver,使其包含事件相应的处理函数的定义,如
    class MObserver
    {
    BOOL virtual HandleEvent( TEvent &aEvent) = 0;
    };

    二、定义一个活动对象会话类(继承自RSessionBase和CActive)如RMySession,正常地包含连接Server和通信的功能,同时让RMySession(可以在构造函数中)接收一个指向MObserver的指针,这样就能在事件发生时调用接口函数来处理事件了。
    class RMySession : public RSessionBase, public CActive

    {
    public:

    ~RMySession ();
    static RMySession * NewL( MObserver *aObserver);
    static RMySession * NewLC( MObserver *aObserver);

    private:
    RMySession ();
    void ConstructL( MObserver *aObserver);

    MObserver* iObserver;
    };

    void RMySession::ConstructL( MObserver *aObserver)
    {
    iObserver = aObserver;
    }

    三、定义一种消息类型如TEvent,用于在Server和Client之间传递事件的信息。在RMySession添加一个TEvent的实例用于接收来自Server的消息。
    class TEvent
    {

    };

    class RMySession
    {

    private:
    TEvent iEvent;
    TPckg< TEvent> iEventPckg;
    };

    RMySession::RMySession : iEventPckg( iEvent)
    {

    };

    四、为RMySession定义两个成员函数StartWaiting和StopWaiting,用于启动和停止对事件的等待。
    void RMySession::StartWaiting()
    {
    iState = EWaiting;
    TIpcArgs p;
    p.Set( 0, iEventPckg);
    SendReceive( EStartWaiting, p, iStatus);
    SetActive();
    }
    void RMKHSession::DoCancel()
    {
    StopWaiting();
    }
    void RMKHSession::StopWaiting()
    {
    TInt rc;
    TIpcArgs p;
    iState = EStopped;
    rc = SendReceive( EStopWaiting, p);
    }

    五、实现RunL,在其中处理到来的事件。
    RMKHSession::RunL()
    {
    if (iState == EStopped)
    {
    return;
    }
    else if (iState != EError)
    {
    StartWaiting();
    SetActive(); // Tell scheduler a request is active
    iObserver->HandleMKHEvent( iEvent);
    }
    }

    六、在Server端,对于到来的EStartWaiting请求暂时将该RMessage的实例缓存起来,等到事件发生时,再改写其携带的TEvent实例的内容,然后用RMessage::Complete( TInt)将消息传递回Client。
    void CServerSession::ServiceL( const RMessage2 &aMessage)
    {
    switch( aMessage.Function())
    {
    case EStartWaiting:
    {
    iCurMsg = new RMessage2( aMessage);
    }
    break;
    case EStopWaiting:
    {
    if( iCurMsg) iCurMsg->Complete( -1);
    iCurMsg = NULL;
    aMessage.Complete(0);
    }
    break;

    }

    }
    void CServerSession::CompleteMessage( TMKHEvent &aEvent)
    {
    if( iCurMsg)
    {
    TPckg<TMKHEvent> pckg( aEvent);
    iCurMsg->Write(0, pckg, 0);
    iCurMsg->Complete( 0);
    delete iCurMsg;
    iCurMsg = NULL;
    }
    }

    这样,就可以实现简单的C/S结构事件驱动程序。

    完毕。

    TinyOS学习简记

    因为项目要使用,我学了一些TinyOS的编程,现在看来还算可以用,所以就将我一路上遇到的问题和我解决问题方法记下,权当备忘。如果对你有用的话,我会非常高兴。

    1、TinyOS开发环境配置

    老实说,TinyOS的环境配置方法网上有不少,比较权威的是www.tinyos.net上的详细讲解,非常详细,特别是对Ubuntu的用户而言入门简单。

    然而,事情并非全如网站上所言,即使依照网站上的配置方法,我也遇到了不少问题。当然,并不一定每个人都会遇到同样的问题,而且很可能大部分人都不会遇到这些问题。

    其中一个比较严重的问题正是伴随着方便的Ubuntu配置而来的。

    使用网站上所提供的两部安装配置方法安装TinyOS后,你会发现Telos系列可以正常的编译,而MICA系列却已编译就报出长长的错误列表。经过我的检查,发现Telos系列可以编译是因为这个系列使用MSP430处理器,而MICA系列使用AVR的ATM128处理器则无法编译。这是因为上述两步安装法会自动配置好MSP430的交叉编译工具,而其AVR交叉编译支持却存在问题,安装完后会发现完全找不到需要的库文件和头文件。

    本来我是打算自己编译AVR-GCC、AVR-LIBC的源码来解决(我实际也是这么做的),但是结果因为我资质愚钝,配了半天也没有搞定。最后我选择了一种投机取巧的方法——下载网站上配好的虚拟系统(xubuntos)……

    的确投机取巧,不过这个保证可以用,-_-|||

    2、关于寄存器的定义

    如果你去翻看TinyOS库文件的内容(/opt/tinyos-xxx/tos/),你就会发现里面常常会用到一些寄存器名(类似宏的写法,用全部大写字母标识,例如MSP430的计时器寄存器TBR和ATM128的计时器1的寄存器TCNT1等),然而你可能找遍库文件目录也找不到哪里有这些寄存器的定义(可以找到它们的存储数据类型的定义,一边是typedef一个int8_t之类的)。

    事实上,你在编译过一次简单的程序(例如示例程序Blink),就可以在build目录对应平台的子目录中找到一个文件app.c。如果你检查这个文件的内容,会发现里面包含了所有你可以使用的寄存器的定义(它们都是volatile变量,而且用__asm直接指定地址)。如果需要参照或者查看自己可以使用的寄存器,可以到app.c文件中寻找。

    3、关于MICAZ+MIB520

    用USB连接MIB520基座后,你会发现MIB520在/dev/目录下占据了两个USB终端位置,其中编号较小的那个是编程下载时使用的,另一个则用于PC和其上的传感器(例如MICAZ)串行通信使用。即如果你使用HplAtm128UartC的Uart0接口与PC进行通信,则收发数据都通过编号较大的USB终端进行。

    Telos系列可以提供精确的32khz时钟,而MICA系列的计时器无法提供精确的32khz计数器,MICA系列实际提供的计数器的频率随处理器时钟变化而变化,一般为31.7khz或者约28.8khz。