KcrusKal、Prime最短路径算法 Unity游戏研究

克鲁斯卡尔算法,克鲁斯卡尔

给定一个带权的无向连通图,如何选取一棵生成树,使树上所有边上权的总和为最小,这叫最小生成树. 求最小生成树的算法

(1) 克鲁斯卡尔算法

图的存贮结构采用边集数组,且权值相等的边在数组中排列次序可以是任意的.该方法对于边相对比较多的不是很实用,浪费时间.

(2) 普里姆算法

图的存贮结构采用邻接矩阵.此方法是按各个顶点连通的步骤进行,需要用一个顶点集合,开始为空集,以后将以连通的顶点陆续加入到集合中,全部顶点加入集合后就得到所需的最小生成树 .


下面来具体讲下:

克鲁斯卡尔算法

方法:将图中边按其权值由小到大的次序顺序选取,若选边后不形成回路,则保留作为一条边,若形成回路则除去.依次选够(n-1)条边,即得最小生成树.(n为顶点数)



第一步:由边集数组选第一条边



第二步:选第二条边,即权值为2的边



第三步:选第三条边,即权值为3的边



第四步:选第四条边,即权值为4的边



第五步:选第五条边

  


普里姆算法

方法:从指定顶点开始将它加入集合中,然后将集合内的顶点与集合外的顶点所构成的所有边中选取权值最小的一条边作为生成树的边,并将集合外的那个顶点加入到集合中,表示该顶点已连通.再用集合内的顶点与集合外的顶点构成的边中找最小的边,并将相应的顶点加入集合中,如此下去直到全部顶点都加入到集合中,即得最小生成树.

例在下图中从1点出发求出此图的最小生成树,并按生成树的边的顺序将顶点与权值填入表中.



———————>先写出其邻接矩阵



第一步:从①开始,①进集合,用与集合外所有顶点能构成的边中找最小权值的一条边

①——②权6

①——③权1 -> 取①——③边

①——④权5   第二步:③进集合,①,③与②,④,⑤,⑥构成的最小边为

①——④权5

③——⑥权4 -> 取③——⑥边 第三步:⑥进集合,①,③,⑥与②,④,⑤构成的各最小边

①——②权6

③——②权5

⑥——④权2 -> 取⑥——④边 第四步:④进集合,①,③,⑥,④与②,⑤构成的各最小边

①——②权6

③——②权5 -> 取③——②边

⑥——⑤权6 第四步:②进集合,①,③,⑥,②,④与⑤构成的各最小边

②——⑤权3 -> 取②——⑤边


这也是在网上找到的一个Kruskal和Prim构造过程图,贴出来:

   


new 发布于 2016-10-9 07:22

Lua中的table函数库 Lua笔记

一部分的table函数只对其数组部分产生影响, 而另一部分则对整个table均产生影响. 下面会分开说明. 


table.concat(table, sep,  start, end)

concat是concatenate(连锁, 连接)的缩写. table.concat()函数列出参数中指定table的数组部分从start位置到end位置的所有元素, 元素间以指定的分隔符(sep)隔开。除了table外, 其他的参数都不是必须的, 分隔符的默认值是空字符, start的默认值是1, end的默认值是数组部分的总长. 

sep, start, end这三个参数是顺序读入的, 所以虽然它们都不是必须参数, 但如果要指定靠后的参数, 必须同时指定前面的参数.

> tbl = {"alpha", "beta", "gamma"}
> print(table.concat(tbl, ":"))
alpha:beta:gamma
> print(table.concat(tbl, nil, 1, 2))
alphabeta
> print(table.concat(tbl, "\n", 2, 3))
beta
gamma

table.insert(table, pos, value)

table.insert()函数在table的数组部分指定位置(pos)插入值为value的一个元素. pos参数可选, 默认为数组部分末尾.

> tbl = {"alpha", "beta", "gamma"}
> table.insert(tbl, "delta")
> table.insert(tbl, "epsilon")
> print(table.concat(tbl, ", ")
alpha, beta, gamma, delta, epsilon
> table.insert(tbl, 3, "zeta")
> print(table.concat(tbl, ", ")
alpha, beta, zeta, gamma, delta, epsilon


table.maxn(table)

table.maxn()函数返回指定table中所有正数key值中最大的key值. 如果不存在key值为正数的元素, 则返回0. 此函数不限于table的数组部分.

> tbl = {[1] = "a", [2] = "b", [3] = "c", [26] = "z"}
> print(#tbl)
3               -- 因为26和之前的数字不连续, 所以不算在数组部分内
> print(table.maxn(tbl))
26
> tbl[91.32] = true
> print(table.maxn(tbl))
91.32


table.remove(table, pos)

table.remove()函数删除并返回table数组部分位于pos位置的元素. 其后的元素会被前移. pos参数可选, 默认为table长度, 即从最后一个元素删起.


table.sort(table, comp)

table.sort()函数对给定的table进行升序排序.

> tbl = {"alpha", "beta", "gamma", "delta"}
> table.sort(tbl)
> print(table.concat(tbl, ", "))
alpha, beta, delta, gamma

comp是一个可选的参数, 此参数是一个外部函数, 可以用来自定义sort函数的排序标准.

此函数应满足以下条件: 接受两个参数(依次为a, b), 并返回一个布尔型的值, 当a应该排在b前面时, 返回true, 反之返回false.

例如, 当我们需要降序排序时, 可以这样写:

> sortFunc = function(a, b) return b < a end
> table.sort(tbl, sortFunc)
> print(table.concat(tbl, ", "))
gamma, delta, beta, alpha

用类似的原理还可以写出更加复杂的排序函数. 例如, 有一个table存有工会三名成员的姓名及等级信息:

guild = {}

table.insert(guild, {
name = "Cladhaire",
class = "Rogue",
level = 70,
})

table.insert(guild, {
name = "Sagart",
class = "Priest",
level = 70,
})

table.insert(guild, {
name = "Mallaithe",
class = "Warlock",
level = 40,
})


对这个table进行排序时, 应用以下的规则: 按等级升序排序, 在等级相同时, 按姓名升序排序.

可以写出这样的排序函数:

function sortLevelNameAsc(a, b)
if a.level == b.level then
return a.name < b.name
else
return a.level < b.level
end
end

测试功能如下:

> table.sort(guild, sortLevelNameAsc)
> for idx, value in ipairs(guild) do print(idx, value.name) end
1, Mallaithe
2, Cladhaire
3, Sagart

table.foreachi(table, function(i, v))
会期望一个从 1(数字 1)开始的连续整数范围,遍历table中的key和value逐对进行function(i, v)操作

t1 = {2, 4, 6, language="Lua", version="5", 8, 10, 12, web="hello lua"};
table.foreachi(t1, function(i, v) print (i, v) end) ; --等价于foreachi(t1, print)

输出结果:
1 2
2 4
3 6
4 8
5 10
6 12

table.foreach(table, function(i, v))
与foreachi不同的是,foreach会对整个表进行迭代

t1 = {2, 4, 6, language="Lua", version="5", 8, 10, 12, web="hello lua"};
table.foreach(t1, function(i, v) print (i, v) end) ;

输出结果:
1 2
2 4
3 6
4 8
5 10
6 12
web hello lua
language Lua
version 5

table.getn(table)
返回table中元素的个数

t1 = {1, 2, 3, 5};
print(getn(t1))
->4

table.setn(table, nSize)
设置table中的元素个数


new 发布于 2016-9-19 08:43

Lua pairs ipairs区别 Lua笔记

ipairs (t)

Returns three values: an iterator function, the table t, and 0, so that the construction

for i,v in ipairs(t) do body end

will iterate over the pairs (1,t[1]), (2,t[2]), ···, up to the first integer key absent from the table.


pairs (t)

Returns three values: the next function, the table t, and nil, so that the construction

for k,v in pairs(t) do body end

will iterate over all key–value pairs of table t.

See function next for the caveats of modifying the table during its traversal.




pairs可以遍历表中所有的key,并且除了迭代器本身以及遍历表本身还可以返回nil;

ipairs则不能返回nil,只能返回数字0,如果遇到nil则退出。

ipairs适用于数组,pairs适用于键值表。


new 发布于 2016-9-19 08:06

skynet研究001——启动流程 Skynet笔记

一篇比较初级的skynet入门教程,见附件。

Skynet框架之菜鸟手册-2.pdf


new 发布于 2016-7-25 09:26

skynet研究001——skynet Skynet笔记

一个由国内大神云风开发的基于C+lua的开源服务器框架(https://github.com/cloudwu/skynet)。

底层核心用c编写,上层逻辑由lua实现。


入门文档   http://skynetdoc.com/book/index.html


源码下载


git clone https://github.com/cloudwu/skynet.git 

安装编译



cd skynet
make macosx

运行服务器demo



./skynet ./examples/config



运行客户端demo


./3rd/lua/lua ./examples/client.lua


输入hello 返回world


标签: skynet

new 发布于 2016-7-25 08:55

Unity与Photon通信简单文档 游戏服务器

Unity与Photon通信简单文档

 

主要的常用相关术语

 

Application
Application指的是游戏逻辑应用,由C#语言书写同时由Photon引擎负责启动运行。所有的应用均从Application继承。

 

Deploy Folder
即发布目录,在服务端SDK中,deploy目录包含了Photon引擎运行的所有需要的文件:Photon核心文件和应用程序文件。   

 

Peer

即连接到Photon引擎的客户端,另一方面Photon服务器端也是Peer,且只有一个。

 

Channel
在Photon引擎中channel主要用于分割通信用,在同一channel中所有的operation和event都是顺序执行的。

 

Disconnect Connect连接)
即服务器和客户端断开操作。通常发生在客户端断开连接或连接超时时候;服务器也可以根据需要断开和客户端之间的连接。

 

Event
Event是异步发送给客户端的事件消息。可以由操作(operations,如
sideeffect)触发或引发(这是operation的主要目的)。事件由事件代码(Eventcode)标识,事件来源则是ActorNumber。

EvCode
即EventCode简称,标识事件的类型以及事件所附带的信息。

 

Operation
对在Photon服务器端上远程方法调用的另外一种叫法。客户端使用operation可以在服务器上做任何事情,甚至可以发送event给其他客户端。

OpCode
“Operation Code”的简称。byte类型,用于触发服务器端操作,客户端获取操作返回结果,用opCodes判断返回动作类型。

 

 

PhotonControl
Photon的管理工具,打开PhotonControl.exe文件即可开启一个托盘应用。

 

PhotonServer.config
Photon引擎的配置文件,主要用于IP、应用以及性能检测设置。以前叫PhotonSocketServer.xml,目前刚刚改为PhotonSocketServer.config。

Timeout
使用eNet方式,客户端和服务器端都监视对方消息是否可靠,如果检测到长时间没有回应,则会断开连接。

 

Unreliable
不可靠的命令则不需要对方回应,它顺序发送数据,可能会有数据丢失,数据序列有“漏洞”。

 

 

通信过程

 

服务器端

 

1.在VS2010环境下建立一个空项目

2.添加引用

   ExitGames.dll;

   Photon.SocketServer.dll;

   PhotonHostRuntimeInterfaces.dll;

 

3.创建一个类命名为Program.cs,添加Using

4.Program继承于ApplicationBase类,并实现其抽象类方法

 

5.添加一个类GamePeer.cs,同上添加Using,该Peer继承PeerBase类,实现其抽象类方法

 

6.生成项目保存到SimpleDemo/bin文件夹下,把SimpleDemo文件夹放到Photon 的deploy文件夹内(具体路径:../你的Photon解压文件夹/deploy)

7.配置Photon,根据你的系统,win7 64位打开deploy/bin_Win64文件夹下的PhotonServer.config文件,用txt打开,找到节点<Applications></Applications>,在此之内添加配置

<Application

                            Name="SimpleDemo"

                            BaseDirectory="SimpleDemo"

                            Assembly="SimpleDemo"

                            Type="SimpleDemo.Program"

                            ForceAutoRestart="true"

                            WatchFiles="dll;config"

                            ExcludeFiles="log4net.config">

                     </Application>

 

保存。 其中Name是应用程序(ServerApp)名字,BaseDirectory是应用程序(SimpleDemo/bin)所在文件夹,Assembly是应用程序集名称(在VS项目属性可以看到),Type指应用程序主程序名(相当于C#中Main函数所在文件名,本例中指Program.cs)(即继承于ApplicationBase类的),后边的是配置文件信息。

 

8.配置好后,打开同级目录下PhotonControl.exe程序即开启Photon程序,然后在任务栏旁边的Photon图标上右键Default/Start as application启动服务器,如果没有问题,Photon图标由灰色变成蓝色,表示启动成功,也可以右键Open Logs打开Log界面,看到Log信息“Server  is running”则服务器启动了。

 

 

 

 

 

 

 

 

客户端(unity3d)

 

1.首先导入Photon SDK(Photon3Unity3D.dll)到unity工程,

 

2.创建客户端Peer类(ClientPeer.cs),添加Using,并继承IPhotonPeerListener类,而且实现其抽象类方法

3.连接到服务器,设置好服务器地址(包括端口号,一般使用Udp是5055)和服务器名称;然后创建Peer进行连接;

本地可以写成(“localhost:5055”),ServerApp指SimpleDemo

在Start()方法里连接服务器

在Update()方法里保持连接

 

 

4.实现ClientPeer中public void OnStatusChanged(StatusCode statusCode)方法,获取连接状态。

运行Unity3d,如果连接畅通,则Debug(“Connected”)信息。

 

 

 

 

通信操作

 

1.客户端向服务器发送数据

 

ClientPeer使用OpCustom方法向服务器发送数据

带三个参数 opCode:操作数,Parameters:内容(字典类型),SendReliable:是否可靠传输;

 

如:按下B,向服务器发送代号为201的操作;

 

    

2.服务端处理客户端请求

 客户端发出的所有服务器请求都会在GamePeer类中的OnOperationRequest方法里接收到(丢包除外),接收到的参数operationRequest包括OpCode和Parameters,通过Opcode判断操作类型;

接收到代号为201的操作后,服务器向客户端发送一个Response(代号为99,带一个字符串数据),该Response只有发送201的Peer才能接收到。服务端也可以向客户端发送一个事件:

 

该事件所有连接上的Peer都能收到代码为100的消息事件。

 

3.客户端处理服务端发来的消息

所有服务端发来的Response消息都会在OnOperationResponse里接收,

 

4.所有服务端发来的Events事件都会在OnEvent里接收到

 

 

 

 

 

 

 

 

 

 


new 发布于 2016-4-19 03:25

写点什么 人生杂谈

有时候总感觉得写点什么,却又不知道该写什么,觉得没什么可写,然后就不了了之了。

曾有多次思如泉涌的瞬间,有很多想表达的东西,关于人生,关于感悟,关于梦想,以为众多的思绪可以变成华丽的诗文,以为可以信手拈来,提笔即是篇章,却奈何腹中只剩零零散散的几个词语,越漂越远。

写点什么无非缅怀过去要么想想未来又或者只是一时冲动,很多时候开始都是想写点什么,然后什么都应该写点,到最后也不知道写的什么。人生又何偿不如是呢?有人说人生这条长河里,左岸是无法忘怀的记忆,右岸是可以憧憬的美好未来,中间流淌着的是值得奋斗的青春和隐隐的伤感。已过弱冠之年,人生正处于急流中间,有很多孩童时的快乐想去找回,却发现面对快步入而立的人生时,只有无能为力和伤感。曾经我一直在想什么是人生,生不带来死不带去的,人生又有什么意义?

过去是美好的。人却总喜欢在失去了的时候才发现曾经的美好,才去怀念。其实,每个人都是第一次做人,没有彩排,每天都不知道会发生什么,每天都是未知,走过那么多的岁月,会发现有很多没有把握的青春,也留下了不少的遗憾,同时也经历风雨变得成熟果敢。后来才想明白,原来人生本没有任何意义,而我们要做的则给人生添加意义。过去的每一天都是积累,是以后丰富人生的度量,。。。

有人说二十五是人生的一个转折,在这个尴尬的年纪,年经又不年经,懂又不懂,一无所有的时候,总以为自己有足够的资本去拼搏,仔细算来却发现还有很多事情没有去尝试,很多儿时的梦想尚未开始,回想当初的踌躇满志,个性与张扬,终究还是年少轻狂。


new 发布于 2016-4-18 02:28

使用Fragment实现底部Tab标签切换界面功能 安卓应用开发

使用Fragment实现底部Tab标签切换界面功能

主界面布局

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context="com.loccy.loccy.MainActivity">
    <RelativeLayout
        android:id="@+id/rl_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <!--<RelativeLayout-->
            <!--android:id="@+id/top_tab"-->
            <!--android:layout_width="match_parent"-->
            <!--android:layout_height="50dip"-->
            <!--android:background="#948a8a">-->
            <!--<ImageView-->
                <!--android:id="@+id/logo"-->
                <!--android:layout_width="match_parent"-->
                <!--android:layout_height="match_parent"-->
                <!--android:layout_centerInParent="true"-->
                <!--android:background="#f00"/>-->
        <!--</RelativeLayout>-->
        <LinearLayout
            android:id="@+id/ll_bottom_tab"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:gravity="center_vertical"
            android:orientation="horizontal"
            android:baselineAligned="true"
            android:background="#dadada">
            <RelativeLayout
                android:id="@+id/rl_tab_homepage"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1">
                <ImageView
                    android:id="@+id/iv_tab_homepage"
                    android:layout_width="26dp"
                    android:layout_height="26dp"
                    android:paddingTop="5dp"
                    android:layout_centerHorizontal="true"
                    android:src="@drawable/tab_homepage_checked"
                    android:contentDescription="@null"/>
                <TextView
                    android:id="@+id/tv_tab_homepage"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:paddingBottom="3dp"
                    android:layout_below="@+id/iv_tab_homepage"
                    android:layout_centerHorizontal="true"
                    android:text="@string/tab_homepage"
                    android:textColor="@color/tab_item_checked"
                    android:textSize="14sp"/>
            </RelativeLayout>
            <RelativeLayout
                android:id="@+id/rl_tab_activity"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1">
                <ImageView
                    android:id="@+id/iv_tab_activity"
                    android:layout_width="26dp"
                    android:layout_height="26dp"
                    android:paddingTop="5dp"
                    android:layout_centerHorizontal="true"
                    android:src="@drawable/tab_me_normal"
                    android:contentDescription="@null"/>
                <TextView
                    android:id="@+id/tv_tab_activity"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:paddingBottom="3dp"
                    android:layout_below="@+id/iv_tab_activity"
                    android:layout_centerHorizontal="true"
                    android:text="@string/tab_activity"
                    android:textColor="@color/tab_item_normal"
                    android:textSize="14sp"/>
            </RelativeLayout>
            <RelativeLayout
                android:id="@+id/rl_tab_message"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1">
                <ImageView
                    android:id="@+id/iv_tab_message"
                    android:layout_width="26dp"
                    android:layout_height="26dp"
                    android:paddingTop="5dp"
                    android:layout_centerHorizontal="true"
                    android:src="@drawable/tab_message_normal"
                    android:contentDescription="@null"/>
                <TextView
                    android:id="@+id/tv_tab_message"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:paddingBottom="3dp"
                    android:layout_below="@+id/iv_tab_message"
                    android:layout_centerHorizontal="true"
                    android:text="@string/tab_message"
                    android:textColor="@color/tab_item_normal"
                    android:textSize="14sp"/>
            </RelativeLayout>
            <RelativeLayout
                android:id="@+id/rl_tab_me"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1">
                <ImageView
                    android:id="@+id/iv_tab_me"
                    android:layout_width="26dp"
                    android:layout_height="26dp"
                    android:paddingTop="5dp"
                    android:layout_centerHorizontal="true"
                    android:src="@drawable/tab_me_normal"
                    android:contentDescription="@null"/>
                <TextView
                    android:id="@+id/tv_tab_me"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:paddingBottom="3dp"
                    android:layout_below="@+id/iv_tab_me"
                    android:layout_centerHorizontal="true"
                    android:text="@string/tab_me"
                    android:textColor="@color/tab_item_normal"
                    android:textSize="14sp"/>
            </RelativeLayout>
        </LinearLayout>
        <View
            android:id="@+id/line"
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:layout_above="@id/ll_bottom_tab"
            android:background="#cfcece">
        </View>
        <LinearLayout
            android:id="@+id/content_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_above="@+id/line"
            android:orientation="vertical">
        </LinearLayout>
    </RelativeLayout>
</android.support.design.widget.CoordinatorLayout>


主Activity控制切换

package com.loccy.loccy;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
    private RelativeLayout tab_homepage_lt,tab_activity_lt,tab_message_lt,tab_me_lt;
    private Fragment homepage_fm,activity_fm,message_fm,me_fm,curFragment;
    private static final int TABCOUNT = 4;
    private ImageView[] tab_item_imgs = new ImageView[TABCOUNT];
    private TextView[] tab_item_txts = new TextView[TABCOUNT];
    private int[] img_ids = new int[]{R.id.iv_tab_homepage,R.id.iv_tab_activity,R.id.iv_tab_message,R.id.iv_tab_me};
    private int[] txt_ids = new int[]{R.id.tv_tab_homepage,R.id.tv_tab_activity,R.id.tv_tab_message,R.id.tv_tab_me};
    private int[] img_nor_ids = new int[]{R.drawable.tab_homepage_normal,R.drawable.tab_me_normal,R.drawable.tab_message_normal,R.drawable.tab_me_normal};
    private int[] img_chd_ids = new int[]{R.drawable.tab_homepage_checked,R.drawable.tab_me_checked,R.drawable.tab_message_checked,R.drawable.tab_me_checked};
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        InitUI();
        InitTab();
    }
    void InitUI(){
        tab_homepage_lt = (RelativeLayout)findViewById(R.id.rl_tab_homepage);
        tab_activity_lt = (RelativeLayout)findViewById(R.id.rl_tab_activity);
        tab_message_lt = (RelativeLayout)findViewById(R.id.rl_tab_message);
        tab_me_lt = (RelativeLayout)findViewById(R.id.rl_tab_me);
        tab_homepage_lt.setOnClickListener(this);
        tab_activity_lt.setOnClickListener(this);
        tab_message_lt.setOnClickListener(this);
        tab_me_lt.setOnClickListener(this);
        for(int i=0;i<TABCOUNT;i++){
            tab_item_imgs[i] = (ImageView)findViewById(img_ids[i]);
            tab_item_txts[i] = (TextView)findViewById(txt_ids[i]);
        }
    }
    void InitTab() {
        if (homepage_fm == null)
            homepage_fm = new HomeFragment();
        if(!homepage_fm.isAdded()){
            getSupportFragmentManager().beginTransaction().add(R.id.content_layout,homepage_fm).commit();
            curFragment = homepage_fm;
            for(int i=0;i<TABCOUNT;i++){
                if(i==0){
                    tab_item_imgs[i].setImageResource(img_chd_ids[i]);
                    tab_item_txts[i].setTextColor(getResources().getColor(R.color.tab_item_checked));
                }
                else {
                    tab_item_imgs[i].setImageResource(img_nor_ids[i]);
                    tab_item_txts[i].setTextColor(getResources().getColor(R.color.tab_item_normal));
                }
            }
        }
    }
    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.rl_tab_homepage:onClickTab(0);break;
            case R.id.rl_tab_activity:onClickTab(1);break;
            case R.id.rl_tab_message:onClickTab(2);break;
            case R.id.rl_tab_me:onClickTab(3);break;
        }
    }
    void onClickTab(int index){
        switch (index){
            case 0:{
                if (homepage_fm == null)
                    homepage_fm = new HomeFragment();
                addOrShowFragment(homepage_fm);
            }
            break;
            case 1:{
                if (activity_fm == null)
                    activity_fm = new ActivityFragment();
                addOrShowFragment(activity_fm);
            }
            break;
            case 2:{
                if (message_fm == null)
                    message_fm = new MessageFragment();
                addOrShowFragment(message_fm);
            }
            break;
            case 3:{
                if (me_fm == null)
                    me_fm = new MeFragment();
                addOrShowFragment(me_fm);
            }
            break;
            default:break;
        }
        for(int i=0;i<TABCOUNT;i++){
            if(i==index){
                tab_item_imgs[i].setImageResource(img_chd_ids[i]);
                tab_item_txts[i].setTextColor(getResources().getColor(R.color.tab_item_checked));
            }
            else {
                tab_item_imgs[i].setImageResource(img_nor_ids[i]);
                tab_item_txts[i].setTextColor(getResources().getColor(R.color.tab_item_normal));
            }
        }
    }
    private void addOrShowFragment(Fragment fragment){
        if(curFragment==fragment)
            return;
        if(!fragment.isAdded()) {
            getSupportFragmentManager().beginTransaction().hide(curFragment).add(R.id.content_layout, fragment).commit();
        }
        else {
            getSupportFragmentManager().beginTransaction().hide(curFragment).show(fragment).commit();
        }
        curFragment = fragment;
    }
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
}

Fragment布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <RelativeLayout
        android:id="@+id/top_tab"
        android:layout_width="match_parent"
        android:layout_height="50dip"
        android:background="#948a8a"
        android:layout_weight="1">
        <ImageView
            android:id="@+id/logo"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_centerInParent="true"
            android:background="#837a7a"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:text="@string/tab_homepage"
            android:textColor="@color/white"
            android:textSize="24sp"/>
        <Button
            android:id="@+id/locate_btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:background="#0000"
            android:text="深圳"
            android:textSize="24sp"/>
    </RelativeLayout>
</LinearLayout>

Fragment代码

package com.loccy.loccy;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.Toast;

/**
* Created by Loccy on 16/3/17.
*/
public class HomeFragment extends Fragment{

private Button search_btn,locate_btn;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//return super.onCreateView(inflater, container, savedInstanceState);

View view = inflater.inflate(R.layout.fragment_homepage,container,false);

locate_btn = (Button)view.findViewById(R.id.locate_btn);
locate_btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(getContext(), "Locate", Toast.LENGTH_SHORT).show();
}
});
return view;
}

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}
标签: Android Tab

new 发布于 2016-3-18 00:40

Lightmap在PC上与android和ios的区别以及解决方法 Unity游戏研究

原文:http://www.ceeger.com/forum/read.php?tid=24457&fid=2

Lightmap在PC上与android和ios的区别以及解决方法

1、  问题描述 

相信很多人碰到过Lightmap的一些问题: 

烘培好Lightmap之后,在PC上看起来相当给力,而打包成ios或android之后,就傻眼了,Lightmap往往就出现了改变,例如灯光曝光度不够、光照颜色偏冷色调、有时候甚至黄色光也能变成绿色光等等。 

2、造成LightmapPCiosandroid上表现不同的原因。 

在u3d里,Lightmap的格式是.exr(openEXR),exr格式的储存方式是使用4*16Bit RGBA来储存数据的,即是说,它使用四个通道分别为RGB和alpha,每个通道占16位储存空间,每个像素占48位储存空间来储存数据。所以EXR格式的图片颜色值域范围就达到了[-65504,65504],远大于8bit(颜色值域[0,255],用浮点数表示就是[0,1])格式所能储存的数值范围。 

但是EXR格式的Lightmap打包成 android或ios之后,就变成LDR格式(可能是单通道8bit)的了,这就丢失的很多光照信息。例如在PC上烘培出的灯光亮度值是2000,转变成LDR格式后,亮度值就变成了255,这就是为什么打包成android或ios后灯光曝光度不够、光照颜色偏冷色调的原因。 

3、 解决方法。 

HDR有几种比较常见的编码格式,这里用的编码格式是LogLuv,就是将RGBA_FP32bit(每个通道32bit或16bit)的数据编码到RGBA32(每个通道8bit)的图片中。LogLuv算法几乎能100%还原unity切换平台后导致Lightmap丢掉的精度信息,算法如下:

   //FP32(RGB) to LogLUV
   fixed4 EncodeLogLuv(fixed3 vRGB)
   {
       fixed3x3 M = fixed3x3(
           0.2209, 0.3390, 0.4184,
           0.1138, 0.6780, 0.7319,
           0.0102, 0.1130, 0.2969 );
       fixed4 vResult;
       fixed3 Xp_Y_XYZp = mul(vRGB, 
M);
       Xp_Y_XYZp = max(Xp_Y_XYZp, 
fixed3(1e-6, 1e-6, 1e-6));
       vResult.xy = Xp_Y_XYZp.xy / 
Xp_Y_XYZp.z;
       fixed Le = 2 * 
log2(Xp_Y_XYZp.y) + 127;
       vResult.w = frac(Le);
       vResult.z = (Le - 
(floor(vResult.w*255.0f)) / 255.0f) / 255.0f;
       return vResult;
}
//LogLuv to FP32(RGB)
   fixed3 DecodeLogLuv(in fixed4 vLogLuv)
   {
       fixed3x3 InverseM = fixed3x3(
          6.0014, -2.7008, -1.7996,
          -1.3320, 3.1029, -5.7721,
          0.3008, -1.0882, 5.6268 );
       fixed Le = vLogLuv.z * 255 + 
vLogLuv.w;
       fixed3 Xp_Y_XYZp;
       Xp_Y_XYZp.y = exp2((Le - 127) 
/ 2);
       Xp_Y_XYZp.z = Xp_Y_XYZp.y / 
vLogLuv.y;
       Xp_Y_XYZp.x = vLogLuv.x * 
Xp_Y_XYZp.z;
       fixed3 vRGB = mul(Xp_Y_XYZp, 
InverseM);
return max(vRGB, 0); 
} 

4、 实现步骤 

1、 在PC平台烘培好光照贴图Lightmap_PC(.exr格式)。 

2、 通过Lightmap_PC生成新的光照贴图Lightmap_LogLuv(.png格式,RGBA32bit,单通道是8bit)。生成过程就用上面提到的LogLuv算法进行编码。这里用GPU来进行生成,有类似GPGPU的思想。如下: 

//用LogLuv算法生成的Lightmap输出到temp上,然后就将temp保存到一张.png格式的图片。
       public bool SaveRenderTextureToPNG(Texture inputTex,Shader outputShader, string contents, string pngName)
{
             RenderTexture temp = RenderTexture.GetTemporary(inputTex.width, inputTex.height, 0, RenderTextureFormat.ARGB32);
             Material mat = new Material(outputShader);
             Graphics.Blit(inputTex, temp, mat);
             bool ret = SaveRenderTextureToPNG(temp, contents,pngName);
             RenderTexture.ReleaseTemporary(temp);
             return ret;
       } 
//将RenderTexture保存成一张png图片
         public bool SaveRenderTextureToPNG(RenderTexture rt,string contents, string pngName)
         {
              RenderTexture prev = RenderTexture.active;
              RenderTexture.active = rt;
              Texture2D png = new Texture2D(rt.width, rt.height, TextureFormat.ARGB32, false);
              png.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0);
              byte[] bytes = png.EncodeToPNG();
              if (!Directory.Exists(contents))
                   Directory.CreateDirectory(contents);
              FileStream file = File.Open(contents + "/" + pngName + ".png", FileMode.Create);
              BinaryWriter writer = new BinaryWriter(file);
              writer.Write(bytes);
              file.Close();
              Texture2D.DestroyImmediate(png);
              png = null;
              RenderTexture.active = prev;
              return true;
         } 

3、 用新生成的光照贴图Lightmap_LogLuv替换原来的光照贴图Lightmap_PC。因为Lightmap_PC在切换android或ios会自动将Lightmap_PC压缩为LDR格式,这就丢失了精度。 

5、编写生成工具

我已经写好了一个简单的工具,可以将当前的场景下所有用Diffuse Shader的模型自动替换成新的Shader,改新的Shader将不再使用Unity原来.EXR格式的光照贴图,而是使用新生成的光照贴图。 

将会在” Assets/Data/Lightmap/当前场景名字”目录下生成对应的光照贴图,如下: 

将当前的场景下所有用Diffuse Shader的模型自动替换成新的Shader,如下:

原文:http://www.ceeger.com/forum/read.php?tid=24457&fid=2


new 发布于 2016-3-11 09:24

UGUI不规则形状按钮 各种技巧

using UnityEngine;
using System;
using System.Collections;
using UnityEngine.UI;
public class RaycastFilter : MonoBehaviour, ICanvasRaycastFilter
{
 private RectTransform rectTransform;
 private Image image;
 public void Awake()
 {
  collider2D = GetComponent<BoxCollider2D>();
  image = GetComponent<Image>();
 }
 public bool IsRaycastLocationValid(Vector2 screenPosition, Camera raycastEventCamera) //uGUI callback
 {
  if (image == null)
   return true;
  Vector2 localPoint;
  RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, screenPosition, raycastEventCamera, out localPoint);
  var normalized = new Vector2(
   (localPoint.x + rectTransform.pivot.x*rectTransform.rect.width)/rectTransform.rect.width,
   (localPoint.y + rectTransform.pivot.y*rectTransform.rect.height)/rectTransform.rect.width);
  Rect rect = image.sprite.textureRect;
  var x = Mathf.FloorToInt(rect.x + rect.width * normalized.x);
  var y = Mathf.FloorToInt(rect.y + rect.height * normalized.y);
  try
  {
   return image.sprite.texture.GetPixel(x,y).a > 0.2f;
  }
  catch (UnityException e)
  {
   Debug.LogError("Mask texture not readable, set your sprite to Texture Type 'Advanced' and check 'Read/Write Enabled'");
   Destroy(this);
   return false;
  }
 }
}
标签: uGUI

new 发布于 2016-3-3 05:51