顯示具有 程式設計 標籤的文章。 顯示所有文章
顯示具有 程式設計 標籤的文章。 顯示所有文章

【MiniMap Kingdom】更新進度 (v256~v270)


2024/4/20

【MiniMap Kingdom】的 v256~v270 版本更新,

這個版本修復了許多 Bug 並新增了更多遊戲內容,

抱歉在醫院住了這麼久,

這個版本也做了很多細節上的調整,

程式和遊戲內容也進行了平衡和改善,

如果您發現任何Bug,請告訴我,

我會盡快修復它,

謝謝。
(っ´ω`c)
----

Unity筆記:取得所有的Text物件

 
public Text[] yourTexts;
public void 更新所有語言翻譯()
{
    yourTexts =
    /*FindObjectsOfType<Text>()*/
    Resources.FindObjectsOfTypeAll<Text>();

......
......
}

這裡有兩種方法可以用
1. FindObjectsOfType<Text>();
2.Resources.FindObjectsOfTypeAll<Text>();


Declaration

public static Object[] FindObjectsOfType(Type type);

Declaration

public static Object[] FindObjectsOfType(Type type, bool includeInactive);

Declaration

public static T[] FindObjectsOfType(bool includeInactive);

Declaration

public static T[] FindObjectsOfType();

Parameters

typeThe type of object to find.
includeInactiveIf true, components attached to inactive GameObjects are also included.

用FindObjectsOfType<Text>()有個問題是新的文檔有一個
"includeInactive"的參數,
但我自己在2019.2的版本中是沒有辦法使用這個參數的,
論壇上也有遇到一樣的問題,
也就是說這個方法在舊版本中,
是只能找尋場景中開啟的物件,


Resources.FindObjectsOfTypeAll

Declaration

public static T[] FindObjectsOfTypeAll();

Description

Returns a list of all objects of Type T.

This function can return any type of Unity object that is loaded, including game objects, prefabs, materials, meshes, textures, etc. It will also list internal objects, therefore be careful with the way you handle the returned objects.

Contrary to Object.FindObjectsOfType this function will also list disabled objects.


Resources.FindObjectsOfTypeAll<Text>()可以避免掉這個問題,
但也就會載入所有東西進去,
如果是做翻譯或改全部的字體還是可以使用的,
當然最好還是一開始就把文字的關聯性處理好,
這是比較亡羊補牢的方法,
還要再驗證一下會不會出其他問題。
(っ´ω`c)

【MiniMap Kingdom】:第三陣營系統。



小地圖王國的第三陣營系統在v191版完工了,

讓遊戲不再只有玩家跟敵人兩種派系,

由於新增多陣營的系統幾乎重寫了整個遊戲的程式,

需要開新存檔才能體驗這個版本的內容,

第三陣營版本是一個過渡性的版本,

有了多陣營的架構後就可以開始做動態的陣營增減了,

需要充足的測試調整後再來完善這個系統,

兩款遊戲都正在努力製作中。
(っ´ω`c)

MiniMap Kingdom 單位圖像優化

單位圖像的存檔區,
遊戲只有這57個單位圖像,
因為效能的關係沒辦法加入更多的動畫,
有興趣優化的人可以先挑一個開始做做看,
有樣本後在Steam或巴哈上傳給作者就可以了。
(っ´ω`c)

圖像的格式:
普通的單位3個,國王6個,劍士4個
預設大小是100*100就好,
但加入遊戲為了動態模糊會放大到2048*2048,
(它會自動生成2048以下的圖層)
直接做成2048*2048放入遊戲會方便一些。

單位圖像的優化目標:
希望能把單位圖像做成淡彩色且容易辨識的狀態,
線條能夠融入背景,
且鏡頭放大模糊時看起來不會太吃力,
如果有任一單位的三張圖,
就可以放入遊戲試試看效果如何了,
詳細請和作者聯絡,謝謝!

自製文字圖表產生器



public string 產生圖表
(
List<long> 紀錄數值, 
List<long> 紀錄時間, 
long 使用單位數, 
string 數值單位名, 
string 時間單位名, 
string 圖表名
)
        {
            string 圖表結果文字 = "";

            string 沒有回傳的描述 =
                "《" + 圖表名 + "圖表紀錄》" + "\n" +
                "" + "\n" +
                "(圖表在有充足資料後顯示)" + "\n" +
                "" + "\n";


            string 回傳值 = "";

            if (紀錄數值.Count >= 1)
            {
                decimal 單位數 = 使用單位數 * 18 / 10;

                decimal 最大值 = 0;

                for (int i = 0; i < 紀錄數值.Count; i++)
                {
                    if (最大值 < 紀錄數值[i])
                    {
                        最大值 = 紀錄數值[i];
                    }
                }

                decimal 均值 = 最大值 / 單位數;

                if (均值 > 0)
                {
                    for (int i = 0; i < 紀錄數值.Count; i++)
                    {
                        string 新增行 = " |  ";

                        decimal 需新增數 = 紀錄數值[i] / 均值;

                        for (int ii = 0; ii < 需新增數; ii++)
                        {
                            新增行 = 新增行 + "-";
                        }

                        //for (int iii = 0; iii < 單位數 - 需新增數; iii++)
                        //{
                        //    新增行 = 新增行 + "  ";
                        //}

                        新增行 = 新增行 + "  |    " + 紀錄時間[i] + 時間單位名 + " ※ " + 數值格式化(紀錄數值[i], 數值單位名);

                        回傳值 = 新增行 + "\n" + 回傳值;
                    }

                    玩家資料.標準彈窗內容Text.alignment = TextAnchor.UpperLeft;

                    圖表結果文字 = "                                                                                            《" + 圖表名 + "圖表紀錄》" + "\n" +
                "" + "\n" +
                "" + 回傳值 + "" + "\n" +
                "" + "\n";



                }
                else
                {
                    圖表結果文字 = 沒有回傳的描述;
                }

            }
            else
            {
                圖表結果文字 = 沒有回傳的描述;
            }

            //==========

            

            玩家資料.是否為查看圖表 = true;

            彈窗事件
                (
                圖表結果文字
                );

            return 回傳值;
        }


約略是2019年寫的一個小工具,
實用在場外人生的圖表系統中,
動態且無上限的將時間與參數用Text排列成可視化圖表,
看到數值的累積除了比較有成就感外,
用於世界產業、景氣變化也讓沙盒世界更有真實波動的感覺,
了解整體的人口、產業、經濟與國際走勢變遷,
簡單在這紀錄一下。
(っ´ω`c)

在unity上初始化翻譯

這篇文章只是想單純記錄一下遇到的問題,
製作翻譯時最初是把字串組放進去,
結果它只記錄了第一次的值,
這是因為引擎已經變成初始化的形狀了,
應該是mono導致的,
現在這樣就可行了,
希望之後也沒有問題。
(つд⊂)

public const int 日本語順序 = 2;

    public static readonly string[] 工人房屋文字翻譯 =  { "WorkerHouse", "工人房屋", "労働者住宅"};
    public static readonly string[] 水井文字翻譯 =  { "WaterWell", "水井", "井戸"};
    public static readonly string[] 農場文字翻譯 =  { "Farm", "農場", "ファーム"};
    public static readonly string[] 伐木場文字翻譯 =  { "WoodWorkshop", "伐木場", "樹木伐採場"};
    public static readonly string[] 採礦場文字翻譯 =  { "MiningCamp", "採礦場", "鉱物採石場"};
    public static readonly string[] 劍士兵營文字翻譯 =  { "SwordsmanCamp", "劍士兵營", "剣士キャンプ"};
    public static readonly string[] 箭塔文字翻譯 =  { "ArcherTower", "箭塔", "アロータワー"};
    public static readonly string[] 學校文字翻譯 =  { "School", "學校", "学校"};
    public static readonly string[] 火槍塔文字翻譯 =  { "GunTower", "火槍塔", "ガンタワー"};
    public static readonly string[] 城堡文字翻譯 =  { "Castle", "城堡", "城"};
    public static readonly string[] 商店文字翻譯 =  { "Shop", "商店", "お店"};
    public static readonly string[] 教堂文字翻譯 =  { "Church", "教堂", "教会"};
    public static readonly string[] 建築工坊文字翻譯 =  { "BuilderWorkshop", "建築工坊", "建設スタジオ"};
    public static readonly string[] 醫院文字翻譯 =  { "Hospital", "醫院", "病院"};
    public static readonly string[] 狗屋文字翻譯 =  { "Doghouse", "狗屋", "犬小屋"};
    public static readonly string[] 城牆文字翻譯 =  { "Wall", "城牆", "城壁"};
    public static readonly string[] 公園文字翻譯 =  { "Park", "公園", "パーク"};
    public static readonly string[] 龍騎兵營文字翻譯 =  { "DragoonCamp", "龍騎兵營", "銃士キャンプ"};
    public static readonly string[] 餐廳文字翻譯 =  { "Restaurant", "餐廳", "ベーカリー"};
    public static readonly string[] 倉庫文字翻譯 =  { "StorageHouse", "倉庫", "倉庫"};

    public static readonly string[] 工人文字翻譯 =  { "Worker", "工人", "労働者"};//
    public static readonly string[] 打水工文字翻譯 =  { "Waterman", "打水工", "セーラー"};
    public static readonly string[] 農夫文字翻譯 =  { "Farmer", "農夫", "農家"};
    public static readonly string[] 木工文字翻譯 =  { "WoodWorker", "伐木工", "木こり"};
    public static readonly string[] 礦工文字翻譯 =  { "Miner", "礦工", "鉱夫"};
    public static readonly string[] 劍士文字翻譯 =  { "Swordsman", "劍士", "剣士"};
    public static readonly string[] 弓箭手文字翻譯 =  { "Archer", "弓箭手", "射手"};
    public static readonly string[] 老師文字翻譯 =  { "Teacher", "老師", "先生"};
    public static readonly string[] 火槍兵文字翻譯 =  { "Gunman", "火槍兵", "銃士"};
    public static readonly string[] 騎士文字翻譯 =  { "Knight", "騎士", "騎士"};
    public static readonly string[] 商店主文字翻譯 =  { "ShopOwner", "商店主", "店主"};
    public static readonly string[] 教士文字翻譯 =  { "Churchman", "教士", "宣教師"};
    public static readonly string[] 建築工文字翻譯 =  { "Builder", "建築工", "ビルダー"};
    public static readonly string[] 醫生文字翻譯 =  { "Doctor", "醫生", "医者"};
    public static readonly string[] 狗文字翻譯 =  { "Dog", "狗", "犬"};
    public static readonly string[] 龍騎兵文字翻譯 =  { "Dragoon", "龍騎兵", "ピストル騎兵隊"};
    public static readonly string[] 廚師文字翻譯 =  { "Chef", "廚師", "シェフ"};

Unity快速設定UI的X,Y和width,height

用最簡單的方式直接設定UI的位置與長寬:

public RectTransform 行動欄位長度;

行動欄位長度.anchoredPosition = new Vector2(0, -1500);//設定位置
行動欄位長度.sizeDelta = new Vector2(1240, 3618);//設定寬長

用這樣的方式就可以通過程式把UI最基本的改成自己想要的狀態了。
(っ´ω`c)

程式設計:如何將指定的String值更改為另一個String字串?(檢查當包含某字串時)

非常簡單的功能,

會用到String.Contains 方法和 String.Replace 方法,
只需要注意String.Replace是回傳新的String,
所以要有賦予的動作才有效就好。
(っ´ω`c)

示例範本:


場外人生的國家系統提供了更改政體的功能,
當角色大權在握時可以更改超過12種的政權架構,
通常國名會綁定政體的名字,但這不是一個強制的系統,
主要是方便玩家取各種國名,
當政體改變時,系統會檢查該國名是否包含政體描述,
如果有的話,就會把國名中舊政體換為新政體,
這樣程式上就兼具了安全性與開放性,
簡單卻相當有效。
(っ´ω`c)

【Unity筆記】如何製作滑鼠停留時的懸浮提示窗?

製作UI的時候如果總是要玩家點進按鈕才能看到功能細節,
最後就會像場外人生一樣不容易直接搞懂,
當然場外人生作為一款文字遊戲,
多少會有需要閱讀的情況,
(つд⊂)


開始製作小地圖王國後,
因為戰略類遊戲需要更大的主畫面空間,
那種把UI資訊塞滿的作法就完全不行了,
這裡我們善用左邊的黑色彈窗,
它預設是關閉的,
當滑鼠移動到特定按鈕上時,
就會跳出來提示玩家,
滑鼠離開後就會關閉,
實際操作上玩家幾乎不會有感覺,
如果你打開像是世紀帝國這類的遊戲,
其實也都善用了這種作法,
由於相當直覺,而且互動性強,
用戶可以快速的了解介面的各種功能,
以及遊戲或系統的設定,
這是電腦上非常常見也好用的UI設計,


在Unity上,我們給按鈕綁上一個Event Trigger的觸發器,
很簡單的加上兩個"滑鼠進入"和"滑鼠離開"的事件,


進入時更新文字並將提示框顯示出來,
離開時就用同個方法關閉,
這樣的話每個按鈕只需要一個自己的文字方法就好了,
做起來其實非常快速,
主要的工作量還是在於給每個按鈕一個描述,
譬如主畫面就有六種資源、十幾個按鈕,
還有十七種建築物、像召集這類的指令也有十幾個,
最好的情況是花時間把他們全部做完,
讓每個可以點選的東西都有提示,
這樣玩家操作起來會非常有回饋感,
但實際上要花的工作量可能不小,
加上翻譯也許要花個兩三天吧,
這幾天先更新一個主要功能提示的版本就好,
至少讓玩家了解各種建築的用途、
指令的提示和主畫面的一些情報,
至少都只是重複的工作量,
慢慢吞下去遊戲就會更完整了,
這些背後的努力也很重要,
好好努力吧。
(つд⊂)

Convert Simplified Chinese to Traditional Chinese (C#)


Please briefly understand the following content before using this program,
Because Simplified Chinese has deleted many useful traditional characters,
If it is directly converted into traditional Chinese, there will be a problem of missing characters.
But the conversion of traditional Chinese into simplified Chinese does not have this problem.
Therefore, we recommend that you directly make a traditional Chinese version when making a Chinese translation.
Although Simplified Chinese is called "Simplified",
But it didn’t make Chinese easier to understand,
It is not friendly to users of traditional Chinese,
Please make independent translations for these two languages,
Use the following program to make this easy.


namespace Chinese
{
    using System.Runtime.InteropServices;

    public static class ChineseProgram
    {
        ///
        /// use "kernel32.dll" to convert
        ///
        private const int LocaleSystemDefault = 0x0800;
        private const int LcmapSimplifiedChinese = 0x02000000;
        private const int LcmapTraditionalChinese = 0x04000000;

        [DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)]
        private static extern int LCMapString(int locale, int dwMapFlags, string lpSrcStr, int cchSrc,
                                              [Out] string lpDestStr, int cchDest);

        public static string ToSimplified(string source)
        {
            string target = new string(' ', source.Length);
            int ret = LCMapString(LocaleSystemDefault, LcmapSimplifiedChinese, source, source.Length, target, source.Length);
            return target;
        }

        public static string ToTraditional(string argSource)
        {
            var t = new string(' ', argSource.Length);
            LCMapString(LocaleSystemDefault, LcmapTraditionalChinese, argSource, argSource.Length, t, argSource.Length);
            return t;
        }

     
    }
}

Unity筆記:如何將2D物件移動至滑鼠點擊的位置?




----
很基本的功能,簡單記錄一下。
----

Vector2.MoveTowards

public static Vector2 MoveTowards
(Vector2 current, Vector2 target, float maxDistanceDelta);

Description

Moves a point current towards target.

This is essentially the same as Vector2.Lerp but instead the function will ensure that the distance never exceeds maxDistanceDelta. Negative values of maxDistanceDelta pushes the vector away from target.


maxDistanceDelta可以理解為每次移動的最大距離,
為負時會向目標位置反向推離。


using UnityEngine;

// 2D MoveTowards example
// Move the sprite to where the mouse is clicked
//
// Set speed to -1.0f and the sprite will move
// away from the mouse click position forever

public class ExampleClass : MonoBehaviour
{
    private float speed = 10.0f;
    private Vector2 target;
    private Vector2 position;
    private Camera cam;

    void Start()
    {
        target = new Vector2(0.0f, 0.0f);
        position = gameObject.transform.position;

        cam = Camera.main;
    }

    void Update()
    {
        float step = speed * Time.deltaTime;

        // move sprite towards the target location
        transform.position = Vector2.MoveTowards(transform.position, target, step);
    }

    void OnGUI()
    {
        Event currentEvent = Event.current;
        Vector2 mousePos = new Vector2();
        Vector2 point = new Vector2();

        // compute where the mouse is in world space
        mousePos.x = currentEvent.mousePosition.x;
        mousePos.y = cam.pixelHeight - currentEvent.mousePosition.y;
        point = cam.ScreenToWorldPoint(new Vector3(mousePos.x, mousePos.y, 0.0f));

        if (Input.GetMouseButtonDown(0))
        {
            // set the target to the mouse click location
            target = point;
        }
    }
}


順帶一提,只要在給予位置後同時把相機位置跟隨正在移動的單位,
加上:
cam.transform.position = transform.position + new Vector3(0,0,-10);
就可以簡單的做出一個跟著滑鼠點擊自由移動且有相機跟隨的物件了,
換句話說,這東西就可以在Unity的世界無盡地自由奔馳了,
當然,實際畫面中需要有相對參考點才能有移動的感覺,
譬如移動到地圖外什麼也沒有的虛空中的話,
觀察者是沒辦法感受到移動的。

另外,通常會改用按鈕之類的UI來當觸發器,
好處是可以讓UI覆蓋在點擊移動的範圍上面,
避免點擊UI的同時也移動到地圖的單位。
(:3ぅ)
----

Unity筆記:自動攻擊最近敵人,箭矢自動指向敵軍射擊。


public Basic 角色腳本;//只是角色的腳本

    private void OnTriggerEnter2D(Collider2D 對象)
    {
        if (!角色腳本.判定對象是否為範圍(對象.gameObject))
//只射擊碰撞到的實際對象,而不是其他物件的攻擊範圍
        {
            角色腳本.角色攻擊敵人(對象.gameObject);

            //Debug.Log(gameObject + "碰撞" + 對象);

        }


    }

    void OnTriggerStay2D(Collider2D 對象)
    {

        if (!角色腳本.判定對象是否為範圍(對象.gameObject))
        {
            角色腳本.角色攻擊敵人(對象.gameObject);

            //Debug.Log(gameObject + "碰撞" + 對象);

        }
    }

    void OnTriggerExit2D(Collider2D 對象)
    {
        if (角色腳本.可攻擊敵人列表.Contains(對象.gameObject))
        {
            角色腳本.可攻擊敵人列表.Remove(對象.gameObject);
        }
    }

//=======================================


 public List<GameObject> 可攻擊敵人列表;
    public void 角色攻擊敵人(GameObject 目標敵人)
    {
        if(是否攻擊敵人 == false)
        {
            float 攻擊速度 = 50 / (1 + 玩家.神力等級() / 3);

            if (目標敵人.GetComponent<itemBasic>().陣營 == 判定.陣營敵人.ToString())
            {
                if (可攻擊敵人列表.Contains(目標敵人) == false)
                {
                    可攻擊敵人列表.Add(目標敵人);
                }

                if (時間秒 - 角色上次攻擊時間 > 攻擊速度)
                {
                    角色上次攻擊時間 = 時間秒;

                    float 暫存距離 = 10000;
                    int 最近敵人 = 0;

                    for (int i = 0; i < 可攻擊敵人列表.Count; i++)
                    {
                        if (可攻擊敵人列表[i] == null)
                        {
                            可攻擊敵人列表.RemoveAt(i);
                            i = 0;
                        }
                        else
                        {
                            float 兩者距離 =
                                Vector2.Distance(player.transform.position, 可攻擊敵人列表[i].transform.position);

                            if (兩者距離 < 暫存距離)
                            {
                                最近敵人 = i;
                                暫存距離 = 兩者距離;
                            }
                        }
                    }


                    //目標敵人 = 可攻擊敵人列表[最近敵人];
                    Debug.Log(可攻擊敵人列表[最近敵人] + "攻擊距離" + 暫存距離);
                    //子彈列表.Add(Instantiate(角色子彈, player.transform.position, new Quaternion(0, 0, 0, 0)));

                    float 生成x位置 = 0;
                    float 生成y位置 = 0;

                    if (可攻擊敵人列表[最近敵人].transform.position.x - player.transform.position.x > 0)
                    {
                        生成x位置 = 0.5f;
                    }
                    else
                    {
                        生成x位置 = -0.5f;
                    }

                    if (可攻擊敵人列表[最近敵人].transform.position.y - player.transform.position.y > 0)
                    {
                        生成y位置 = 0.5f;
                    }
                    else
                    {
                        生成y位置 = -0.5f;
                    }

                    GameObject 暫存新子彈 = Instantiate(角色子彈, new Vector3(player.transform.position.x + 生成x位置, player.transform.position.y + 生成y位置, 0), new Quaternion(0, 0, 0, 0));

                    暫存新子彈.GetComponent<playerBullet>().遊戲管理員 = this;
                    暫存新子彈.GetComponent<playerBullet>().子彈發射(可攻擊敵人列表[最近敵人]);
                    暫存新子彈.GetComponent<playerBullet>().生成時間 = 時間秒;


                    for (int i = 0; i < 可攻擊敵人列表.Count; i++)
                    {
                        if (可攻擊敵人列表[i] == null)
                        {
                            可攻擊敵人列表.RemoveAt(i);
                            i = 0;
                        }
                        else
                        {
                            float 兩者距離 =
                                Vector2.Distance(player.transform.position, 可攻擊敵人列表[i].transform.position);

                            if (兩者距離 > 100)
                            {
                                可攻擊敵人列表.RemoveAt(i);
                                i = 0;
                            }
                        }
                    }
                }
            }
        }

        
    }

//===============================

 public void 子彈發射(GameObject 目標)
    {
        float 目標角度x = 目標.transform.position.x;
        float 目標角度y = 目標.transform.position.y;

        Vector2 目標向量 = new Vector2
(transform.position.x - 目標角度x, 
transform.position.y - 目標角度y);

        //RETURN一個角度值
        float angleZ = Mathf.Atan2(目標向量.y, 目標向量.x) * Mathf.Rad2Deg;

        //把角度值傳給物件
        transform.eulerAngles = new Vector3(0, 0, angleZ + 90);


        //Instantiate(特效, transform.position, transform.rotation);

        單純移動單位(this.gameObject, 20, 目標.transform.position, 遊戲管理員.時間秒, 100, true);
    }

//==============================


程式只是記一下而已,別太在意,
一開始其實直接用OnTriggerStay2D就可以自動攻擊了,
但是這樣沒辦法做出"最近"敵人的效果,
只用Trigger判定會出現鎖定一個敵人後就停不下來了,
土法煉鋼的話網路上是直接弄一個enemylist,
用tag之類的屬性每次尋遍一次,
但是對象有上百上千個時這種做法很吃效能,
還是要用Trigger於實際遇到目標時再弄可攻擊敵人的列表會更好一些,

分成三階段,
1.角色索敵範圍發現敵人,
使用Trigger進入與停在範圍內時發動攻擊,
2.發動攻擊時新增列表,在列表中挑出最近的可攻擊敵人攻擊,
3.攻擊後並且旋轉箭矢,面對敵人射擊,
4.將過遠、已摧毀、已離開攻擊範圍的單位移出列表,

中間卡了一陣子原來是把新增敵人列表的方法寫在了射擊速度延遲後才判定,
其實只要保持有碰撞時立刻判定就不會有這種問題了,
之後來弄個弓箭手、箭塔還有城堡好了,
箭矢有穿透兼少量的擊退效果和沒射中時插在地上一陣子的狀態感覺不錯。
(っ´ω`c)

Unity筆記:用程式更改Sprite和Animator設定Write Default衝突的小問題。


通過程式更改Sprite應該是一件很簡單的事情,

public Sprite mySprite;

this.GetComponent<SpriteRenderer>().sprite = mySprite;

不過這天實際操作起來卻一直沒辦法正確反應,
論壇上另一種作法是載入資源,
不過這兩種寫法應該是幾乎一樣的,
研究了半天才想到應該是物件有Animator覆蓋了預設的sprite,


這個隱藏在animator中每個動畫的小設定Write Default,
取消後它就不會在動畫結束後跑回原本的sprite了,
之前的問題是因為程式雖然成功更改了sprite,
但動畫管理員在播完後又把它改了回去,
有時還會一抖一抖的,
取消後就完全正常了,
唯一需要注意的是,
Write Default會回到預設的那個sprite,
譬如動畫是從0~1~2~3這樣,
之前如果沒有特別寫0~1~2~3~0也會變回去,
取消後就要自己再補一幀或者直接用程式在動畫播完後改想要的sprite也可以,
總之,可以在動畫撥放前後、以及靜態的情況下自由更改物件的sprite,
基本上就完全掌握了物件的圖像表現方式了。
(*´﹃`*)

Write Default其實還有更複雜的動畫銜接問題,
不過這天處理的只是動畫與更改sprite的關係,
另外,其實也可以直接用動畫來更改想表現的sprite,
說起來有點繞口,總之最終的表現方式是滿意的且效能良好就可以了。
ლ(´ڡ`ლ)

Unity筆記:音量滾動條之方法選擇輸入預設值的小問題




音量滾動條在場外人生已經做過一次了,
不過新遊戲卻用一樣的腳本卡了兩三個小時,
問題出在滾動條方法中的On Value Chang多了一個預設值,
明明場外人生中就沒有的讓人很疑惑啊,
搞了半天回頭一想才發現同個滾動條的方法中有"動態"和"輸入值"兩種,
動態會顯示在最上面,並且不會有預設值才對,
一般方法的話就等同是用固定的預設值來操作了,
要讓滾動條的On Value Chang生效要選擇動態才行,
總之弄好就好了,
開發做這種系統功能性的東西有時候挺繁瑣的,
但不做又不太行,
總之拿出耐心做好吧。
(っ´ω`c)

Unity筆記:地圖邊界敵人生成器。





【小地圖王國】的單位是會無限的畫出地塊的,
並且有趨向玩家的一個慣性存在,
所以把玩家放在中央演示出來,
會形成這樣的圖案,
偶爾自行走出去的單位增加邊界,
邊界生成的單位又走回來到中心,
由於邊界是可以無限浮動的,
所以隨著進行地圖會緩緩的擴張,

定義:

levelX為地圖的最大x值,
最小levelX為地圖的最小x值,

y值同理


public Vector2 地圖邊界位置()
    {
        int 隨機值 = Random.Range(1,5);

        float 生成位置x = levelX;
        float 生成位置y = levelY;

        if (隨機值 == 1)
        {
            生成位置x = levelX;
            生成位置y = Random.Range(最小levelY, levelY + 1);
        }
        else if(隨機值 == 2)
        {
            生成位置x = Random.Range(最小levelX, levelX + 1); ;
            生成位置y = levelY;
        }
        else if (隨機值 == 3)
        {
            生成位置x = 最小levelX;
            生成位置y = Random.Range(最小levelY, levelY + 1);
        }
        else if (隨機值 == 4)
        {
            生成位置x = Random.Range(最小levelX, levelX + 1); ;
            生成位置y = 最小levelY;
        }
        else
        {
            Debug.Log("【地圖邊界位置小錯誤!】");
        }

        return new Vector2(生成位置x, 生成位置y);
    }

可以優化成switch之類的不過沒有關係,

之後要再測試一下有沒有額外的問題,
不過這種生成器應該只會給殭屍之類的單位來用,
平常還是會依天數從特定方向生成一批單位,
然後由AI建造建築來生產敵人會比較有趣,
也能做出攻打聚落和城市的感覺。
(っ´ω`c)

Unity筆記:存檔系統


----------
2020/6/26
----------
花了一個早上把小地圖王國的存檔系統給做完了,
很多人大概覺得遊戲的存檔系統只要按個鍵什麼的就會生出來了,
但實際做過遊戲的人都知道存檔其實是開發中前幾麻煩的事情,
像Unity這樣的引擎,它的預設類還是沒有辦法直接序列化的,
所以必須把資料類和引擎類分開,
在開發場外人生時我就已經痛苦過一次了,
這次開發小地圖王國更多的是上百個獨立單位和腳本,
存檔系統也會稍微複雜一點,
話是這麼說,不過有了之前的經驗,
只掙扎了一個早上就完整做出來了,
唉,人的成長有時候就是這樣,
努力著努力著就熟練了,
最後放棄思考也能把它做完,
效率也蠻高的,
測試上百次最後看到沒問題時應該是很有成就感的,
不過到後面整個人已經有點萎縮起來了,
好累啊,想脫離苦海的感覺,
我真的不是那種喜歡寫程式的人,
這種存檔系統尤其,
不過一回生二回熟,
下次寫這種東西可能就直接複製就好了,
所以把今天寫出來還沒整理又亂七八糟的東西先記在這吧,
至少這是可以完美存檔的。
(つд⊂)
----------
存檔分為幾個部分:

1.固定的存檔:
舉例來說,玩家進了遊戲順手調整了音效,
那麼可以預期下次他打開遊戲時希望系統自動讀取了上次調整的音效,
這部分的"設定"屬於固定的自動存檔,
玩家雖然沒感覺,
但還是要存起來。

2.主腳本的存檔:
包含了遊戲管理員、玩家和世界的資源、
系統項目甚至是玩家物件都放在這裡。

3.遊戲中的物件:
相對複雜的部份,
首先,可以預期遊戲中會有不少動態生成的物件,
像小地圖王國這種建造沙盒類的尤其如此,
生產的單位、建築都屬於這類,
Unity非但沒有那種一鍵儲存物件的功能,
相反的物件資料是不能直接序列化的,
一些關聯性和繼承unity類的腳本都要手動的重新讀取,
資源站上當然有很多號稱可以一鍵讀取的腳本,
但不論如何,到了最後還是要自己寫接口把自己的資料存進去才行,
如果沒有特別的需求,
自己寫說不定還少點麻煩,
至少我只花了一個上午就從零開始做完了ww。
(早上05~11點)

4.地圖和其他東西的存檔:
這些東西不一定能序列化,
資料需要整個重建才行,
稍微有一點點麻煩,
不過量相較起來會少一點。
----------
開始前要意識到一件事情,
像存檔系統這種東西愈早寫完愈好,
如果很晚才寫,
那麼之後要改架構工作量就會成指數成長,
很高興這次有了之前的開發經驗沒有再走回頭路,
直接把專案開始後的基本架構給設計好了,
真的感到自己成長了啊,
QAQ
----------
總之,我把這個上午寫完的東西貼在這裡,
亂七八糟的只留給自己參考,
其實核心只有序列化跟反序列化而已,
真的要做並不困難,
做完之後就一勞永逸了,
每個單位只要調用同一個方法就會自動存檔了。
(つд⊂)
----------
固定存檔:

[System.Serializable]
public class 固定存檔
{
    public Basic 玩家資料二;


    public string 是否有存檔 = "";


    public float 音樂音量存檔 = 1.0f;
    public float 音效音量存檔 = 1.0f;
    public void 儲存音樂音量()
    {
        音樂音量存檔 = 玩家資料二.背景音樂管理員.volume;
        Save();
    }
    public void 儲存音效音量()
    {
        音效音量存檔 = AudioListener.volume;
        Save();
    }
   
   
    public void 讀取音量存檔()
    {
        玩家資料二.調整音樂音量(音樂音量存檔);
        玩家資料二.調整音效音量(音效音量存檔);
    }

    public string 語言模式;

    public void Save()
    {
        玩家資料二 = GameObject.Find("GameManager").GetComponent<Basic>();

        var serializedData = JsonUtility.ToJson(玩家資料二.固定存檔);

        byte[] serizliedData = System.Text.Encoding.UTF8.GetBytes(serializedData);

        var filePath = Application.persistentDataPath + "/save/" + "staticSave.dat";

        Debug.Log("存檔位置:" + filePath);

        System.IO.File.WriteAllBytes(filePath, serizliedData);
    }

    public void Load()
    {
        玩家資料二 = GameObject.Find("GameManager").GetComponent<Basic>();

        string dirPath = Application.persistentDataPath + "/save";
        if (System.IO.Directory.Exists(dirPath))
        {
            Debug.Log("已有存檔資料夾:" + dirPath);
        }
        else
        {
            System.IO.Directory.CreateDirectory(dirPath);
            Debug.Log("創建存檔資料夾:" + dirPath);
        }

        var filePath = Application.persistentDataPath + "/save/" + "staticSave.dat";

        Debug.Log("存檔位置:" + filePath);

        try
        {
            var serizliedData = System.IO.File.ReadAllBytes(filePath);

            var serializedData = System.Text.Encoding.UTF8.GetString(serizliedData);

            JsonUtility.FromJsonOverwrite(serializedData, 玩家資料二.固定存檔);
        }
        catch (System.IO.FileNotFoundException)
        {
            Debug.Log("讀檔無存檔文件");
        }

        玩家資料二 = GameObject.Find("GameManager").GetComponent<Basic>();
    }
}
----------
主腳本存檔:

public void Save()
    {
        玩家名 = 玩家資料.玩家基本單位管理員.單位資料.單位名稱;

        玩家x = 玩家資料.player.transform.position.x;
        玩家y = 玩家資料.player.transform.position.y;

        var serializedData = JsonUtility.ToJson(玩家資料.玩家);

        byte[] serizliedData = System.Text.Encoding.UTF8.GetBytes(serializedData);

        var filePath = Application.persistentDataPath + "/save/save1/" + "playerSave.dat";

        Debug.Log("存檔位置:" + filePath);

        System.IO.File.WriteAllBytes(filePath, serizliedData);

   
        //地圖存檔區===============================
        var mapserializedData = JsonUtility.ToJson(new Serialization<string, string>(地塊位置對應地塊名字));

        byte[] mapserizliedData = System.Text.Encoding.UTF8.GetBytes(mapserializedData);

        var mapfilePath = Application.persistentDataPath + "/save/save1/" + "mapSave.dat";

        Debug.Log("地圖存檔位置:" + mapfilePath);

        System.IO.File.WriteAllBytes(mapfilePath, mapserizliedData);
        //===============================

        //單位資料存檔區===============================
        var itemdataserializedData = JsonUtility.ToJson(new Serialization<string, string>(存檔次序對應單位類型));

        byte[] itemdataserizliedData = System.Text.Encoding.UTF8.GetBytes(itemdataserializedData);

        var itemdatafilePath = Application.persistentDataPath + "/save/save1/" + "itemdataSave.dat";

        Debug.Log("單位資料存檔位置:" + itemdatafilePath);

        System.IO.File.WriteAllBytes(itemdatafilePath, itemdataserizliedData);
        //===============================
    }

    public void Load()
    {
        var filePath = Application.persistentDataPath + "/save/save1/" + "playerSave.dat";

        Debug.Log("存檔位置:" + filePath);

        try
        {
            var serizliedData = System.IO.File.ReadAllBytes(filePath);

            var serializedData = System.Text.Encoding.UTF8.GetString(serizliedData);

            JsonUtility.FromJsonOverwrite(serializedData, 玩家資料.玩家);
        }
        catch (System.IO.FileNotFoundException)
        {
            Debug.Log("讀檔無存檔文件");
        }

        //地圖讀檔區=====================
        var mapfilePath = Application.persistentDataPath + "/save/save1/" + "mapSave.dat";

        Debug.Log("地圖存檔位置:" + mapfilePath);

        try
        {
            var mapserizliedData = System.IO.File.ReadAllBytes(mapfilePath);

            var mapserializedData = System.Text.Encoding.UTF8.GetString(mapserizliedData);

            地塊位置對應地塊名字 = JsonUtility.FromJson<Serialization<string, string>>(mapserializedData).ToDictionary();
        }
        catch (System.IO.FileNotFoundException)
        {
            Debug.Log("讀檔無存檔文件");
        }
        //==============

        //單位資料讀檔區=====================
        var itemdatafilePath = Application.persistentDataPath + "/save/save1/" + "itemdataSave.dat";

        Debug.Log("單位資料存檔位置:" + itemdatafilePath);

        try
        {
            var itemdataserizliedData = System.IO.File.ReadAllBytes(itemdatafilePath);

            var itemdataserializedData = System.Text.Encoding.UTF8.GetString(itemdataserizliedData);

            存檔次序對應單位類型 = JsonUtility.FromJson<Serialization<string, string>>(itemdataserializedData).ToDictionary();
        }
        catch (System.IO.FileNotFoundException)
        {
            Debug.Log("讀檔無存檔文件");
        }
        //==============
    }
----------
用到的工具類,
Unity的Json現在應該是可以存list的,
不過泛型不行,
還有字典也不行,
所以要調用以下的工具類來把它存進去,
用到字典的部分包含了地圖的地址對地塊名,
還有所有需存檔物件的存檔編號對應單位名稱,
泛型list應該是沒有用到才對。

// Serialization.cs
// List<T>
[System.Serializable]
public class Serialization<T>
{
    [SerializeField]
    List<T> target;
    public List<T> ToList() { return target; }

    public Serialization(List<T> target)
    {
        this.target = target;
    }
}

// Dictionary<TKey, TValue>
[System.Serializable]
public class Serialization<TKey, TValue> : ISerializationCallbackReceiver
{
    [SerializeField]
    List<TKey> keys;
    [SerializeField]
    List<TValue> values;

    Dictionary<TKey, TValue> target;
    public Dictionary<TKey, TValue> ToDictionary() { return target; }

    public Serialization(Dictionary<TKey, TValue> target)
    {
        this.target = target;
    }

    public void OnBeforeSerialize()
    {
        keys = new List<TKey>(target.Keys);
        values = new List<TValue>(target.Values);
    }

    public void OnAfterDeserialize()
    {
        var count = System.Math.Min(keys.Count, values.Count);
        target = new Dictionary<TKey, TValue>(count);
        for (var i = 0; i < count; ++i)
        {
            target.Add(keys[i], values[i]);
        }
    }
}
----------
單位存檔:

[System.Serializable]
public class 單位資料
{
    public itemBasic 單位資料腳本;

    public float 上次生產單位時間;
    public float 上次工作時間;

    public string 陣營;
    public string 單位細節;
    public long 血量;
    public long 血量上限;
    public float 上次恢復血量時間;
    public long 攻擊力;

    public string 單位名稱;
    public long 單位經驗;

    public float 單位x;
    public float 單位y;

    public void 存檔()
    {
        單位x = 單位資料腳本.gameObject.transform.position.x;
        單位y = 單位資料腳本.gameObject.transform.position.y;

        int 存檔次序 = 單位資料腳本.遊戲管理員.玩家.存檔次序對應單位類型.Count;

        單位資料腳本.遊戲管理員.玩家.存檔次序對應單位類型.Add
            (
            存檔次序.ToString()
            ,
            單位細節
            );

        Save(存檔次序);
    }

    public void Save(int 單位次序)
    {
        var serializedData = JsonUtility.ToJson(單位資料腳本.單位資料);

        byte[] serizliedData = System.Text.Encoding.UTF8.GetBytes(serializedData);

        var filePath = Application.persistentDataPath + "/save/save1/" + 單位次序 + "Save.dat";

        Debug.Log("存檔位置:" + filePath);

        System.IO.File.WriteAllBytes(filePath, serizliedData);
    }

    public void Load(int 單位次序)
    {
        var filePath = Application.persistentDataPath + "/save/save1/" + 單位次序 + "Save.dat";

        Debug.Log("存檔位置:" + filePath);

        try
        {
            var serizliedData = System.IO.File.ReadAllBytes(filePath);

            var serializedData = System.Text.Encoding.UTF8.GetString(serizliedData);

            JsonUtility.FromJsonOverwrite(serializedData, 單位資料腳本.單位資料);
        }
        catch (System.IO.FileNotFoundException)
        {
            Debug.Log("讀檔無存檔文件");
        }
    }
}
----------

地圖存檔和最後保存和讀取的接口:

public void 存檔按鈕()
    {
        string dirPath = Application.persistentDataPath + "/save/save1";

        if (System.IO.Directory.Exists(dirPath))
        {
            System.IO.Directory.Delete(dirPath,true);
            Debug.Log("刪除已有存檔資料夾:" + dirPath);

            System.IO.Directory.CreateDirectory(dirPath);
            Debug.Log("創建存檔資料夾:" + dirPath);
        }
        else
        {
            System.IO.Directory.CreateDirectory(dirPath);
            Debug.Log("創建存檔資料夾:" + dirPath);
        }


        GameObject[] 需存檔單位列表 = GameObject.FindGameObjectsWithTag("save");

        玩家.存檔次序對應單位類型 = new Dictionary<string, string>();

        for (int i = 0; i < 需存檔單位列表.Length; i++)
        {
            itemBasic 存檔單位基本管理員 = 玩家基本單位管理員.對象基本腳本(需存檔單位列表[i]);

            存檔單位基本管理員.存檔();
        }

        玩家.Save();

       



        固定存檔.是否有存檔 = 玩家.是();
    }
    public void 讀檔按鈕()
    {
        if(固定存檔.是否有存檔 == 玩家.是())
        {
            初始化();

            玩家.Load();

            玩家基本單位管理員.單位資料.單位名稱 = 玩家.玩家名;

            player.transform.position = new Vector2(玩家.玩家x,玩家.玩家y);

            for (int y = 玩家.最小levelY; y < 玩家.levelY; y++)
            {
                for (int x = 玩家.最小levelX; x < 玩家.levelX; x++)
                {
                    string 位置文字 = 轉換地塊位置為文字(x, y);

                    if (玩家.地塊位置對應地塊名字.ContainsKey(位置文字))
                    {
                        tilemap.SetTile
                    (new Vector3Int((int)轉換地塊文字為位置(位置文字).x, (int)轉換地塊文字為位置(位置文字).y, 0),
                    地塊名字對應地塊物件[玩家.地塊位置對應地塊名字[位置文字]]);
                    }
                }
            }

            for (int i = 0; i < 玩家.存檔次序對應單位類型.Count; i++)
            {
                string key = i.ToString();

                if (玩家.存檔次序對應單位類型.ContainsKey(key))
                {
                    if (玩家.存檔次序對應單位類型[key] == 判定.單位細節玩家劍士.ToString())
                    {
                        GameObject 暫存單位 = 生成基本劍士玩家方法(player.transform.position);

                        對象讀檔(i, 暫存單位);
                    }
                    else if (玩家.存檔次序對應單位類型[key] == 判定.單位細節敵人劍士.ToString())
                    {
                        GameObject 暫存單位 = 生成基本劍士敵人方法(player.transform.position);

                        對象讀檔(i, 暫存單位);
                    }
                    else if (玩家.存檔次序對應單位類型[key] == 判定.單位細節玩家工人.ToString())
                    {
                        GameObject 暫存單位 = 生成基本工人玩家方法(player.transform.position);

                        對象讀檔(i, 暫存單位);
                    }
                    else if (玩家.存檔次序對應單位類型[key] == 判定.單位細節玩家工人房屋.ToString())
                    {
                        GameObject 暫存單位 = 生成基本工人房屋玩家方法(player.transform.position);

                        對象讀檔(i, 暫存單位);
                    }

                }
            }

            更新圖標人口與消耗量();
            更新所有資源();

            詢問名字.SetActive(false);
            主選單第一層.SetActive(true);

            主選單.SetActive(false);
        }
        else
        {
            更新作者的話
                (
                "No saved file found!" + "\n" +
                "沒有找到存檔!" + "\n"
                );
        }
    }

    public void 對象讀檔(int i,GameObject 對象)
    {
        itemBasic 對象基本腳本 = 玩家基本單位管理員.對象基本腳本(對象);

        對象基本腳本.單位資料.Load(i);

        對象基本腳本.gameObject.transform.position =
            new Vector2
            (
                對象基本腳本.單位資料.單位x,
                對象基本腳本.單位資料.單位y
            );

        對象基本腳本.單位資料.單位資料腳本 = 對象基本腳本;
    }
----------

就這樣!
做完了好開心啊,
這幾天可以火力全開來寫新單位了,
我有強烈的預感這會是一款神作,
希望這種感覺能持久一點。
(〃∀〃)ゞ

Unity筆記:Steam成就系統


----
(2020/6/30)
----
之前場外人生弄了一百個成就,
其實邊玩邊解鎖還蠻有趣的,
小地圖王國也會弄一系列,
不過這次會留一些來做之後的成就,
總之要上架前還是要先做點成就系統,
這樣上架時的頁面才能標註有成就支援,
(如果沒有做完就給他審的話他會要你先把它撤掉或做完)
不過如果你先撤掉再給他審完再補上去好像就可以了(??),
總之遲早要做,開始吧:

https://steamworks.github.io/

installation - Releases - 下載Unitypackage

直接丟進專案,

把Assets\Scripts\Steamworks.NET的SteamManager

中的

// If Steam is not running or the game wasn't started through Steam, SteamAPI_RestartAppIfNecessary starts the
// Steam client and also launches this game again if the User owns it. This can act as a rudimentary form of DRM.

// Once you get a Steam AppID assigned by Valve, you need to replace AppId_t.Invalid with it and
// remove steam_appid.txt from the game depot. eg: "(AppId_t)480" or "new AppId_t(480)". 1360670
// See the Valve documentation for more information: https://partner.steamgames.com/doc/sdk/api#initialization_and_shutdown

/*if (SteamAPI.RestartAppIfNecessary(AppId_t.Invalid))*/

改成

if (SteamAPI.RestartAppIfNecessary((AppId_t)你的APPID,就是商店頁面網址的那個值))
{
Application.Quit();
return;
}


改好後,只要在場景中創個空物件,把SteamManager丟進去,
(該物件將不會被摧毀)

之後其他腳本調用只要用using Steamworks就可以了,


解鎖與清空成就的程式:

public void 解鎖成就(string 對應編號)
    {
        SteamUserStats.SetAchievement(對應編號);
        SteamUserStats.StoreStats();
    }
    public void 清除所有成就()
    {
        SteamUserStats.ResetAllStats(true);
        SteamUserStats.StoreStats();
    }


再來就是到後台去把成就設定弄好,
弄那個設定意外花時間的,
要一直等它網頁載入,
不過一勞永逸,等等就把它做完吧。
(っ´ω`c)
----

Unity筆記:為UI.Text字體加上邊框





原本以為沒有的功能,
其實只要加個組件就有了,
這對浮動的UI文字很有幫助,
不如說沒有邊框的話很多時候會被地圖之類的東西給模糊掉,
有邊框的話就很清楚了。

Outline

The Outline component adds a simple outline effect to graphic components such as Text or Image. It must be on the same GameObject as the graphic component.
Text with no effect. Text with Outline effect.

Properties

Property:Function:
Effect ColorThe color of the outline.
Effect DistanceThe distance of the outline effect horizontally and vertically.
Use Graphic AlphaMultiplies the color of the graphic onto the color of the effect.

Unity筆記:單位採集AI系統。


說到AI感覺都很複雜的樣子,
不過好的AI應該是愈簡單有效愈好,
這點對有幾百個單位的小地圖王國這種遊戲為了效能優化上尤其重要,
Unity到目前也還沒有真正的2d尋路支援,
但是,用地塊和字典還是能簡單的做出有效的單位自動尋路系統,
讓我簡單演示一下:


 public Vector2 漫遊位置(Vector2 輸入值, bool 是否需要指定地塊, string[] 指定地塊名列表)
    {
        Vector2 回傳值 = new Vector2();

        回傳值 = 輸入值 + new Vector2(Random.Range(-3, 4), Random.Range(-3, 4));

        bool 是否結束重新尋找地塊 = false;

        if (是否需要指定地塊)
        {
            for (int i = 0; i < 指定地塊名列表.Length; i++)
            {
                if(玩家.地塊位置對應地塊名字[轉換地塊位置為文字(回傳值.x, 回傳值.y)] == 指定地塊名列表[i])
                {
                    是否結束重新尋找地塊= true;
                }
            }

            if(是否結束重新尋找地塊 == false && Random.Range(1, 40) != 1)
            {
                回傳值 = 漫遊位置(輸入值, 是否需要指定地塊, 指定地塊名列表);
            }
        }

        return 回傳值;
    }


首先單位會傳入自己的位置,
目標是回傳一個附近的資源點,
之後再由單位自己移動到那個資源點,

單位傳入位置後,會有一個是否值來決定要不要尋找資源,
一些負責防禦的單位譬如劍士之類的就不需要了,
但農夫、打水工、伐木工和礦工這類的就會希望他們自己去找附近還有的資源,
否則可能還會卡在護城河之類的地方,
其他像是學者、商人和教士,
也能做成會去尋找有道路跟建築的地方,
(也只能在城市中產生資源),
來達成都市化的目標,

有了這個是否值後,
我們再傳入一個目標的地塊列表,
因為有些單位不會只想要找一種地塊,
譬如工人,除了負責採收食物外,
還需要幫忙礦工挖砂子找礦物,
所以就要傳入農地、綠地、沙地,三種地塊名稱,
傳入後他就會判定回傳的位置是否為這三種地的其中一種,
如果都不是,就回頭再弄一個新的位置出來,

細節上這個代碼是找附近6*6的位置,
重複判定用了個隨機值1~39,
這不是好的寫法,但增加點隨機性也完整遍歷完附近的地塊也不錯,
尤其實際遊戲中,
單位畢竟會待在資源點附近,
所以也不用太擔心他跑到其他地方,
如果真的沒有找到他就會走到原本的目標去,
下一次找的時候又會有新的6*6土地可以去跑了,

雖然只是少少的幾行程式,
但加進遊戲後效果很好啊,
至少工人不再是無腦的亂走了,
建設資源區之類的也明顯有了回饋,
寫出AI系統的感覺真棒。
(〃∀〃)ゞ

Unity筆記:執行所有擁有特定程式碼的程式方法。



很基本的功能,
不過在做視野拉大自動把所有單位細節UI隱藏的功能時還翻了一下,
這種做法很吃效能,只能放在像玩家手動操作視野範圍這種由玩家觸發的行動上,
請不要放在update之類的地方,
多數理想情況還是要隨時把程式物件拿在手上,
不過單位多的話偶爾也需要用這種硬性的遍歷物件。


public void 更新所有單位狀態UI()
    {
        GameObject[] obj = FindObjectsOfType(typeof(GameObject)) as GameObject[];
        foreach (GameObject 物件 in obj)   
        {
            //Debug.Log(物件.gameObject.name);

            itemBasic 單位腳本 = 物件.GetComponent<itemBasic>();

            if (單位腳本 != null)   
            {
                單位腳本.更新單位名稱等級();
                單位腳本.更新單位狀態("");
                單位腳本.血量變化(0);
            }
        }
    }