使用 Visual Studio Code 編譯C# 方法:
- 下載 Visual Studio Code 後進行安裝
- 並安裝C# 相關套件
- 再來 Visual Studio Code 新增資料夾到工作區
- 終端機 打上: dotnet new console
使用 Visual Studio Code 編譯C# 方法:
這邊程式碼,只張貼過程程式碼。
OnClickView 類別 (計算過程)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class OnClickView : CalculatoOnClck
{
private bool active = false;
// 顯示計算過程
private static string PROCESS, RESULT, VALUE;
private static double A, SUM;
private bool acrive = false;
private static string CHEAR_MESSAGE; //CHEAR_MESSAGE;
private static int CHEAR_STATUS; // CHEAR_STATUS
// 計算過程
public static string RESULT_PROCESS()
{
return "過程: " + PROCESS;
}
// 計算結果
public static string RESULT_()
{
return "結果:" + RESULT;
}
// 數學符號
void chear(string chear)
{
active = true;
CHEAR_MESSAGE = chear;
if (PROCESS.Substring(PROCESS.Length) != CHEAR_MESSAGE)
PROCESS += CHEAR_MESSAGE;
}
// 數字訊息
void numberMessage(double conNumber)
{
if (active == true)
{
PROCESS = "0";
active = false;
}
if (RESULT == null || RESULT == "")
PROCESS += conNumber;
else
PROCESS = RESULT + CHEAR_MESSAGE + conNumber;
VALUE += conNumber;
}
public void addClick()
{
chear("+");
CHEAR_STATUS = 1;
if (RESULT == null || RESULT == "")
A = double.Parse(VALUE);
else
A = double.Parse(RESULT);
VALUE = "0";
}
public void chearClick()
{
throw new System.NotImplementedException();
}
public void eightClick()
{
numberMessage(8);
}
public void equalClick()
{
if (CHEAR_STATUS == 1)
SUM = A + double.Parse(VALUE);
if (CHEAR_STATUS == 2)
SUM = A - double.Parse(VALUE);
if (CHEAR_STATUS == 3)
SUM = A * double.Parse(VALUE);
if (CHEAR_STATUS == 4)
SUM = A / double.Parse(VALUE);
RESULT = SUM.ToString();
}
public void fiveClick()
{
numberMessage(5);
}
public void fourClick()
{
numberMessage(4);
}
public void nineClick()
{
numberMessage(9);
}
public void oneClick()
{
numberMessage(1);
}
public void reduceClick()
{
chear("-");
CHEAR_STATUS = 2;
if (RESULT == null || RESULT == "")
A = double.Parse(VALUE);
else A = double.Parse(RESULT);
VALUE = "0";
}
public void removeClick()
{
chear("÷");
CHEAR_STATUS = 4;
if (RESULT == null || RESULT == "")
A = double.Parse(VALUE);
else A = double.Parse(RESULT);
VALUE = "0";
}
public void sevenClick()
{
numberMessage(7);
}
public void sixClick()
{
numberMessage(6);
}
public void takeClick()
{
chear("×");
CHEAR_STATUS = 3;
if (RESULT == null || RESULT == "")
A = double.Parse(VALUE);
else A = double.Parse(RESULT);
VALUE = "0";
}
public void threeClick()
{
numberMessage(3);
}
public void twoClick()
{
numberMessage(2);
}
public void zeroClick()
{
numberMessage(0);
}
}
結果圖:
Unity 練習UI 腳本,因此做了一個小型居家照顧小工具,主要家人工作關係,都要計算,就拿來當練習玩玩。
專案會持續更新.....會弄到導航計算公里數功能。
顯示結果:
本文 只提供場景的UI 功能程式。
主頁
MainInterface 腳本:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class MainInterface
{
private Image buttonArea;
private RectTransform area;
ImageSetting buttonAreaSetting;
AreaSetting areaSetting;
public MainInterface(Image buttonArea, RectTransform area)
{
this.buttonArea = buttonArea;
this.area = area;
}
public void buttonArea_Layout()
{
buttonAreaSetting = new ImageSetting(buttonArea, 1f, 1f, 1.6f, 1.6f);
buttonAreaSetting.function(null, false, true, area);
// buttonArea 物件底下子物件
area.transform.parent = buttonArea.transform;
areaSetting = new AreaSetting(area, new Vector2(0, 0), new Vector2(1, 1), new Vector2(0.5f, 0.5f));
areaSetting.function(TextAnchor.MiddleCenter, false, false, false, false, true, true);
}
}
------------------------
服務時間計算
ServiceInterface 腳本:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System;
public class ServiceInterface
{
private RectTransform manage;
private Text hour, minute, titleMessage, serviceMessage;
private InputField hourInput, minuteInput, serviceInputField;
AreaSetting manageSetting;
TextSetting titleMessageSetting;
InterfaceSetting hourSetting, minuteSetting, servicesSetting;
private Image breakGroud;
private Image breaGroudArea;
ImageSetting breakGroudSetting;
ImageSetting breakGraoudAreaSetting;
public ServiceInterface(RectTransform manage, Text titleMessage,
Text hour, InputField hourInput, Text minute, InputField minuteInput)
{
this.manage = manage;
this.titleMessage = titleMessage;
this.hour = hour;
this.hourInput = hourInput;
this.minute = minute;
this.minuteInput = minuteInput;
}
public ServiceInterface(Image breakGroud, Image breaGroudArea, RectTransform manage)
{
this.breakGroud = breakGroud;
this.breaGroudArea = breaGroudArea;
this.manage = manage;
}
public ServiceInterface(RectTransform manage, Text serviceMessage, InputField serviceInputField)
{
this.manage = manage;
this.serviceMessage = serviceMessage;
this.serviceInputField = serviceInputField;
}
public void breakGroud_Layout()
{
breakGroudSetting = new ImageSetting(breakGroud, 1f, 1f, 1.6f, 1.6f);
breakGroudSetting.function(null);
// breakGroudArea 物件底下子物件
breaGroudArea.transform.parent = breakGroud.transform;
breakGraoudAreaSetting = new ImageSetting(breaGroudArea, 1f, 1.4f, 1.6f, 0.8f);
breakGraoudAreaSetting.function(null, false, true, manage);
// breaGroudArea 物件底下子物件
manage.transform.parent = breaGroudArea.transform;
manageSetting = new AreaSetting(manage, Vector2.zero, new Vector2(1, 1), new Vector2(0.5f, 1));
manageSetting.defaultFunction(new Vector2(0, Screen.height / 2 * 0.8f));
}
// 服務次數
public void serviceTime_Layout(Font font, float pointY)
{
manageSetting = new AreaSetting(manage, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f));
manageSetting.defaultFunction(Vector2.zero);
servicesSetting = new InterfaceSetting(serviceMessage, serviceInputField, font);
servicesSetting.funtcionHoliday("服務次數", pointY, Color.green);
serviceMessage.transform.parent = manage.transform;
serviceInputField.transform.parent = manage.transform;
}
public void serverTime_Layout(Font font, float pointY, string title)
{
manageSetting = new AreaSetting(manage, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f));
manageSetting.defaultFunction(Vector2.zero);
titleMessageSetting = new TextSetting(titleMessage, 0.6f, pointY, 0.58f, 0.1f);
titleMessageSetting.function(font, FontStyle.Normal, title, TextAnchor.MiddleLeft, Color.green, 7);
hourSetting = new InterfaceSetting(hour, hourInput, font);
hourSetting.dateWeekfunction("小時", pointY - 0.16f, Color.red);
minuteSetting = new InterfaceSetting(minute, minuteInput, font);
minuteSetting.dateWeekfunction("分鐘", pointY - 0.36f, Color.red);
// manage 物件底下子物件
titleMessage.transform.parent = manage.transform;
hour.transform.parent = manage.transform;
hourInput.transform.parent = manage.transform;
minute.transform.parent = manage.transform;
minuteInput.transform.parent = manage.transform;
titleMessage.transform.parent = manage.transform;
}
public int getServiceValue()
{
if (serviceInputField.text == "")
return 1;
return Int32.Parse(serviceInputField.text);
}
public int getMinuteValue()
{
if (minuteInput.text == "")
return 0;
return Int32.Parse(minuteInput.text);
}
public int getHourValue()
{
if (hourInput.text == "")
return 0;
return Int32.Parse(hourInput.text);
}
}
------------------------
單位換算
TimeConversionInterface 腳本:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System;
using UnityEngine.Events;
public class TimeConversionInterface
{
private Text hour, minute, calculatorProcess, calculatorResult;
private InputField hourInput, minuteInput;
private RectTransform hourManage, minuteMange, calculatorArea;
AreaSetting hourMangeSetting, minuteMangeSetting, calculatorAreaSetting;
InterfaceSetting hourSetting, minuteSetting;
public TimeConversionInterface(RectTransform hourManage, Text hour, InputField hourInput,
RectTransform minuteMange, Text minute, InputField minuteInput)
{
this.hourManage = hourManage;
this.hour = hour;
this.hourInput = hourInput;
this.minuteMange = minuteMange;
this.minute = minute;
this.minuteInput = minuteInput;
}
private Image breakGroud, instructionBreakGroud;
private Text instruction;
ImageSetting breakGroudSetting, instructionBreakGroudSetting;
TextSetting instructionSetting;
public TimeConversionInterface(Image breakGroud, Image instructionBreakGroud, Text instruction)
{
this.breakGroud = breakGroud;
this.instructionBreakGroud = instructionBreakGroud;
this.instruction = instruction;
}
TextSetting calculatorProcessSetting, calculatorResultSetting;
public TimeConversionInterface(Image breakGroud, RectTransform calculatorArea, Text calculatorProcess, Text calculatorResult)
{
this.breakGroud = breakGroud;
this.calculatorArea = calculatorArea;
this.calculatorProcess = calculatorProcess;
this.calculatorResult = calculatorResult;
}
private Button button1, button2, button3, button4;
ButtonSetting button1Setting, button2Setting, button3Setting, button4Setting;
public TimeConversionInterface(Button button1, Button button2, Button button3, Button button4)
{
this.button1 = button1;
this.button2 = button2;
this.button3 = button3;
this.button4 = button4;
}
public void calculatorHorizontalBtns(float pointY, Font font, string btnStr1, string btnStr2, string btnStr3, string btnStr4,
UnityAction unityAction1, UnityAction unityAction2, UnityAction unityAction3, UnityAction unityAction4)
{
button1Setting = new ButtonSetting(button1, 0.4f, 0.7f + pointY, 0.3f, 0.15f, unityAction1);
button1Setting.function(font, FontStyle.Normal, btnStr1, TextAnchor.MiddleCenter, Color.black, 7);
button2Setting = new ButtonSetting(button2, 0.8f, 0.7f + pointY, 0.3f, 0.15f, unityAction2);
button2Setting.function(font, FontStyle.Normal, btnStr2, TextAnchor.MiddleCenter, Color.black, 7);
button3Setting = new ButtonSetting(button3, 1.2f, 0.7f + pointY, 0.3f, 0.15f, unityAction3);
button3Setting.function(font, FontStyle.Normal, btnStr3, TextAnchor.MiddleCenter, Color.black, 7);
button4Setting = new ButtonSetting(button4, 1.6f, 0.7f + pointY, 0.3f, 0.15f, unityAction4);
button4Setting.function(font, FontStyle.Normal, btnStr4, TextAnchor.MiddleCenter, Color.black, 7);
}
// 計算機介面
public void calculayoutSetting(Font font, float povitY, int textSize)
{
breakGroudSetting = new ImageSetting(breakGroud, 1f, 0.6f, 1.6f, 0.66f);
breakGroudSetting.function(null, false, true, calculatorArea);
calculatorAreaSetting = new AreaSetting(calculatorArea, Vector2.zero, new Vector2(1, 1), new Vector2(0.5f, 1f));
calculatorAreaSetting.defaultFunction(new Vector2(1, Screen.height / 2 * povitY));
// 顯示過程
calculatorProcessSetting = new TextSetting(calculatorProcess, 1f, 0.86f, 1.5f, 0.1f);
calculatorProcessSetting.function(font, FontStyle.Normal, "", TextAnchor.MiddleLeft, Color.black, 7);
// 顯示計算後結果
calculatorResultSetting = new TextSetting(calculatorResult, 1f, 0.76f, 1.5f, 0.1f);
calculatorResultSetting.function(font, FontStyle.Normal, "", TextAnchor.MiddleLeft, Color.black, 7);
}
// 背景
public void breakGroud_Layout(string instructionMessage)
{
breakGroudSetting = new ImageSetting(breakGroud, 1f, 1f, 1.6f, 1.6f);
breakGroudSetting.function(null);
instructionBreakGroudSetting = new ImageSetting(instructionBreakGroud, 1f, 1.59f, 1.6f, 0.42f);
instructionBreakGroudSetting.function(null, false, true, instruction.rectTransform);
// instructionBreakGroud 物件底下子物件
instruction.transform.parent = instructionBreakGroud.transform;
instructionSetting = new TextSetting(instruction, 0, 0, 0, 0.74f);
instructionSetting.function(7, instructionMessage, new Vector2(0, 0f), new Vector2(1, 1), new Vector2(0.5f, 1f));
}
// 小時與分鐘
public void hourAndminute(Font font, float pointY)
{
hourMangeSetting = new AreaSetting(hourManage, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f),
new Vector2(0.5f, 0.5f));
hourMangeSetting.defaultFunction(Vector2.zero);
// hourManage 物件底下子物件
hour.transform.parent = hourManage.transform;
hourInput.transform.parent = hourManage.transform;
hourSetting = new InterfaceSetting(hour, hourInput, font);
hourSetting.dateWeekfunction("小時", pointY, Color.red);
minuteMangeSetting = new AreaSetting(minuteMange, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f),
new Vector2(0.5f, 0.5f));
minuteMangeSetting.defaultFunction(Vector2.zero);
// minuteMange 物件底下子物件
minuteMange.transform.parent = minuteMange.transform;
minuteInput.transform.parent = minuteMange.transform;
minuteSetting = new InterfaceSetting(minute, minuteInput, font);
minuteSetting.dateWeekfunction("分鐘", pointY - 0.15f, Color.red);
}
public int getHourValue()
{
if (hourInput.text == "")
return 0;
return Int32.Parse(hourInput.text);
}
public int getMinuteValue()
{
if (minuteInput.text == "")
return 0;
return Int32.Parse(minuteInput.text);
}
}
------------------------
週次計算
InterfaceObj 腳本:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public abstract class InterfaceObj
{
protected Text message;
protected InputField inputFiled;
protected Font font;
public InterfaceObj(Text message, InputField inputFiled, Font font)
{
this.message = message;
this.inputFiled = inputFiled;
this.font = font;
}
public abstract void dateWeekfunction(string textMessage, float pointY);
public abstract void funtcionHoliday(string textMessage, float pointY);
public abstract void systemAndYear(string textMessage, float pointX);
}
InterfaceSetting 腳本:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System;
public class InterfaceSetting : InterfaceObj
{
public InterfaceSetting(Text messge, InputField inputFiled, Font font) : base(messge, inputFiled, font) { }
TextSetting textSetting;
InputFieldSetting inputFieldSetting;
// 週次
public override void dateWeekfunction(string textMessage, float pointY)
{
textSetting = new TextSetting(message, 0.5f, pointY, 0.3f, 0.1f);
textSetting.function(font, FontStyle.Normal, textMessage, TextAnchor.MiddleLeft, Color.red, 7);
inputFieldSetting = new InputFieldSetting(inputFiled, 1.15f, pointY + 0.01f, 0.9f, 0.1f);
inputFieldSetting.function(font, FontStyle.Normal, TextAnchor.MiddleLeft, Color.black, 11,
InputField.ContentType.IntegerNumber);
}
// 假期
public override void funtcionHoliday(string textMessage, float pointY)
{
textSetting = new TextSetting(message, 0.65f, pointY, 0.6f, 0.1f);
textSetting.function(font, FontStyle.Normal, textMessage, TextAnchor.MiddleLeft, Color.red, 7);
inputFieldSetting = new InputFieldSetting(inputFiled, 1.28f, pointY + 0.01f, 0.64f, 0.1f);
inputFieldSetting.function(font, FontStyle.Normal, TextAnchor.MiddleLeft, Color.black, 11,
InputField.ContentType.IntegerNumber);
}
// 年與月
public override void systemAndYear(string textMessage, float pointX)
{
textSetting = new TextSetting(message, pointX, 1.89f, 1f, 0.13f);
textSetting.function(font, FontStyle.Normal, textMessage, TextAnchor.MiddleLeft, Color.red, 7);
inputFieldSetting = new InputFieldSetting(inputFiled, pointX - 0.65f, 1.89f, 0.3f, 0.12f);
inputFieldSetting.function(font, FontStyle.Normal, TextAnchor.MiddleLeft, Color.black, 11,
InputField.ContentType.IntegerNumber);
}
}
外部 Dll 都屬於非Unity的套件,加上Unity API 依賴 .net 架構,所以每一個版本Unity 支援的 .NET 版本 會有些不同。
因此 我們要對於專案的設定如下:
※ 匯入Dll 檔案,非Unity內部功能,會導致錯誤,因此要更改。
Unity 2019 LTS - 2020 做法:
Unity 環境下執行可正常,發佈不正常的情況下,大多數都是缺少:
那些dll檔案在Unity 安裝路徑下都能找的到,以下路徑 2019 LTS 路徑
路徑: C:\Program Files\Unity 2019.4.34f1\Editor\Data\MonoBleedingEdge\lib\mono\unityjit
就能尋找的到了。
本篇使用自然語言處理其中最基礎的一環,就是斷詞。斷詞方法有很多,這裡只舉長詞優先。長詞優先,主要中文特性的規範存在「字→詞→句」構成。需要由詞的部分開始下手。
所先需要資料庫存放詞語數據,再由演算法比對的方式來呈現。得到結果後就可以做出各式各樣的功能 (如: 關鍵字搜尋)。
Hyphenation 腳本(斷詞類別)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
public abstract class Hyphenation
{
protected List<string> ward;
protected string dataBaseTitlle;
public Hyphenation(List<string>ward, string dataBaseTitlle)
{
this.ward = ward;
this.dataBaseTitlle = dataBaseTitlle;
}
public abstract List<string> reverse(MySQLFunction mySQLFunction);
public abstract List<string> forward(MySQLFunction mySQLFunction);
public abstract List<string> speech(MySQLFunction mySQLFunction);
}
HyphenationSetting 腳本
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class HyphenationSetting : Hyphenation
{
private int forwardWardNumber = 0;
private int reverceWardNumber = 0;
public HyphenationSetting(List<string> ward, string dataBaseTitlle) : base(ward, dataBaseTitlle) { }
// 正向
public override List<string> forward(MySQLFunction mySQLFunction)
{
List<string> list = new List<string>();
list.Clear();
forwardWardNumber = 0;
while (ward.Count >= 1)
{
if (forwardWardNumber < ward.Count)
{
if (ward[forwardWardNumber] != "")
{
for (int y = 0; y <= ward[forwardWardNumber].Length; y++)
{
string ForwardMessage = ward[forwardWardNumber].Substring(0, ward[forwardWardNumber].Length - y);
if (ForwardMessage == mySQLFunction.inquire(dataBaseTitlle, 0, ForwardMessage))
{
list.Add(ForwardMessage);
ward[forwardWardNumber] = ward[forwardWardNumber].Remove(0, ForwardMessage.Length);
}
else if (ForwardMessage.Length == 1 && ward[forwardWardNumber] != mySQLFunction.inquire(dataBaseTitlle, 0, ForwardMessage))
{
list.Add(ForwardMessage);
ward[forwardWardNumber] = ward[forwardWardNumber].Remove(0, ForwardMessage.Length);
}
}
}
else
forwardWardNumber += 1;
}
else ward.Clear();
}
return list;
}
// 反向
public override List<string> reverse(MySQLFunction mySQLFunction)
{
List<string> list = new List<string>();
list.Clear();
reverceWardNumber = 0;
while (ward.Count >= 1)
{
if (reverceWardNumber < ward.Count)
{
if (ward[reverceWardNumber] != "")
{
for (int y = 0; y <= ward[reverceWardNumber].Length; y++)
{
string ReverseMessage = ward[reverceWardNumber].Substring(y);
if (ReverseMessage == mySQLFunction.inquire(dataBaseTitlle, 0, ReverseMessage))
{
list.Add(ReverseMessage);
ward[reverceWardNumber] = ward[reverceWardNumber].Substring(0, ward[reverceWardNumber].Length - ReverseMessage.Length);
}
else if (ReverseMessage.Length <= 1 && ReverseMessage != mySQLFunction.inquire(dataBaseTitlle, 0, ReverseMessage))
{
list.Add(ReverseMessage);
ward[reverceWardNumber] = ward[reverceWardNumber].Substring(0, ward[reverceWardNumber].Length - ReverseMessage.Length);
}
}
}
else
reverceWardNumber += 1;
}
else ward.Clear();
}
return list;
}
// 詞性
public override List<string> speech(MySQLFunction mySQLFunction)
{
List<string> list = new List<string>();
foreach (string str in ward)
{
if (mySQLFunction.inquire(dataBaseTitlle, 1, str) != "")
list.Add(mySQLFunction.inquire(dataBaseTitlle, 1, str));
else list.Add("?");
}
return list;
}
}
---------
專案設定 :
※ 匯入Dll 檔案,非Unity內部功能,會導致錯誤,因此要更改。
Unity 2019 LTS - 2020 做法:
設計介面:
以下3張圖片:
常常看到手機遊戲都有Joystick 圖示來控制角色移動,按鈕控制角色: 跳躍,攻擊.....等等的功能,大概花時間研究了一下,可以用UnityUI的屬性方式來進行。
操作之前,我們可以理解成介面都是由圖片方式來進行與操作者互動。
例如:
以上圖示Joystick 圖示按鈕
結果:
以下藉由Unity.EventSystems interface 方式來進行撰寫功能。
Joystick -- C# 腳本
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
public class Joystick : MonoBehaviour, IDragHandler, IPointerUpHandler, IPointerDownHandler
{
// Init
protected Image container;
protected Image joystick;
protected Vector3 InputDirection = Vector3.zero;
public void OnDrag(PointerEventData ped)
{
Vector2 position = Vector2.zero;
// Get InputDirection
RectTransformUtility.ScreenPointToLocalPointInRectangle(
container.rectTransform,
ped.position,
ped.pressEventCamera,
out position
);
float x = (position.x / container.rectTransform.sizeDelta.x);
float y = (position.y / container.rectTransform.sizeDelta.y);
InputDirection = new Vector3(x, y, 0);
InputDirection = (InputDirection.magnitude > 1) ? InputDirection.normalized : InputDirection;
// Define the area in which joystick can move around
joystick.rectTransform.anchoredPosition = new Vector3(
InputDirection.x * container.rectTransform.sizeDelta.x / 3,
InputDirection.y * container.rectTransform.sizeDelta.y / 3
);
}
public void OnPointerDown(PointerEventData ped)
{
OnDrag(ped);
}
public void OnPointerUp(PointerEventData ped)
{
InputDirection = Vector3.zero;
joystick.rectTransform.anchoredPosition = Vector3.zero;
}
}
InterfaceOBJ -- C# 腳本
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Events;
public class InterfaceOBJ
{
private Image imageObj;
private Button buttonObj;
private interfaceObj interfaceObj;
public InterfaceOBJ(Image imag, interfaceObj interfaceObj)
{
this.imageObj = imag;
this.interfaceObj = interfaceObj;
}
public InterfaceOBJ(Button button, interfaceObj interfaceObj)
{
this.buttonObj = button;
this.interfaceObj = interfaceObj;
}
// image
public void image(float pointX, float pointY, float sizeX, float sizeY)
{
interfaceObj.image(imageObj, pointX, pointY, sizeX, sizeY);
}
// button
public void button(float pointX, float pointY, float sizeX, float sizeY, UnityAction onClick)
{
interfaceObj.button(buttonObj, pointX, pointY, sizeX, sizeY, onClick);
}
}
public interface interfaceObj
{
void image(Image image, float pointX, float pointY, float sizeX, float sizeY);
void button(Button button, float pointX, float pointY, float sizeX, float sizeY, UnityAction onClick);
}
InterfaceObj -- C# 腳本
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
public class InterfaceObj : interfaceObj
{
public void button(Button button, float pointX, float pointY, float sizeX, float sizeY, UnityAction onClick)
{
try
{
button.transform.position = new Vector2(Screen.width / 2 * pointX, Screen.height / 2 * pointY);
button.image.rectTransform.sizeDelta = new Vector2(Screen.width / 2 * sizeX, sizeY);
button.onClick.AddListener(onClick);
}
catch { }
}
public void image(Image image, float pointX, float pointY, float sizeX, float sizeY)
{
try
{
image.rectTransform.position = new Vector2(Screen.width / 2 * pointX, Screen.height / 2 * pointY);
image.rectTransform.sizeDelta = new Vector2(Screen.width / 2 * sizeX, Screen.height / 2 * sizeY);
}
catch { }
}
}
Layout_Main -- C# 腳本
public class Layout_Main : Joystick
{
public Image fixedJoystick_image, handle_image;
public Button jump_btn;
InterfaceOBJ fixedJoystick, jump;
public static Vector3 DIRCTION;
public static bool JUMP_SW;
// 計時器
private float timeCount;
private float timeCD = 1f;
public static bool JUMP_ACTIVE;
private void Start()
{
base.container = fixedJoystick_image;
base.joystick = handle_image;
fixedJoystick = new InterfaceOBJ(fixedJoystick_image, new InterfaceObj());
fixedJoystick.image(0.23f, 0.4f, 0.2f, 0.35f);
jump = new InterfaceOBJ(jump_btn, new InterfaceObj());
jump.button(1.68f, 0.42f, 0.2f, 80, jump_onClkck);
}
private void Update()
{
DIRCTION = base.InputDirection;
JUMP_ACTIVE = jumpCd();
}
void jump_onClkck()
{
if (JUMP_ACTIVE == true)
{
JUMP_SW = true;
timeCount = timeCD;
}
}
// 跳躍冷卻時間
public bool jumpCd()
{
return ((timeCount -= Time.deltaTime) <= 0) ? true : false;
}
}
Player -- C# 腳本
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
Rigidbody rigidbody;
private float gravity = 10.0f, maxVelocityChange = 50f, jumpHeight = 1f;
public float speed;
void Start()
{
rigidbody = GetComponent<Rigidbody>();
rigidbody.freezeRotation = true;
rigidbody.useGravity = false;
}
private void FixedUpdate()
{
if(Layout_Main.DIRCTION.magnitude != 1)
{
Vector3 position = new Vector3(Layout_Main.DIRCTION.x * speed, 0,
Layout_Main.DIRCTION.y * speed);
position = transform.TransformDirection(position);
Vector3 velocity = rigidbody.velocity;
Vector3 velocityChange = (position - velocity);
velocityChange.x = Mathf.Clamp(velocityChange.x, -maxVelocityChange, maxVelocityChange);
velocityChange.z = Mathf.Clamp(velocityChange.z, -maxVelocityChange, maxVelocityChange);
velocityChange.y = 0;
rigidbody.AddForce(velocityChange, ForceMode.VelocityChange);
if (Layout_Main.JUMP_SW)
rigidbody.velocity = new Vector3(velocity.x, CalculateJumpVerticalSpeed(), velocity.z);
if (Layout_Main.JUMP_ACTIVE == false)
Layout_Main.JUMP_SW = false;
}
rigidbody.AddForce(new Vector3(0, -gravity * rigidbody.mass, 0));
}
// Jump
float CalculateJumpVerticalSpeed()
{
return Mathf.Sqrt(2 * jumpHeight * gravity);
}
}
Unity 介面設計:
Canvas 物件:
Player 物件:
又嘗試用Unity 做其他事情,最近看到班表這個有趣的事情,之前有貼過固定班格式與如何讀取日期的文章。
想要挑戰一下,完美的功能,如: 有日期、公司、選擇班別,班表內容是否超過當日的營業時間與休假跟加班計算....等功能。
本人比較懶,因此很少調整Unity 設計畫面,大致上都以程式碼部分直接調整,以下所使用的方式由Interface方式來呈現。
-----以下成功畫面-----
------------接下來就是 程式碼部份了------------
ExcelClass
腳本 (C#) :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public abstract class ExcelClass
{
public abstract class ClassInterface
{
public abstract class Preset
{
public abstract void classFixed(int year, int month, string companyName, string unit, int staffNumber);
public abstract void excelSave(int staffNumber);
}
public abstract class Formula
{
public abstract void createClass(int year, int month, string companyName, string unit, int staffNumber, int operAtingHours,
bool dayShift, bool night, bool bigNight);
public abstract void excelSave(int satffNumber);
}
}
}
PresetClass 腳本
using System.IO;
using OfficeOpenXml;
using System;
using System.Collections.Generic;
public class PresetClass : ExcelClass.ClassInterface.Preset
{
// 建立資料位置
private string folderPath = @"D:\班表\";
private string filePath;
// 檔案類別
FileInfo fileInfo;
Calendar.GetCalender getCalender = new GetCalender();
// excel 組件
public static ExcelPackage EXCEL_PACKAGE;
public static ExcelWorksheet WORKSHEET;
// Excel 固定格式 (年、職位、天、資料寫入縱)
int companyNameYear = 1;
int positionNumber = 2;
int dayExcelNuber = 3;
int dataExcelNum = 4;
public static List<Data_date> GET_Data = new List<Data_date>();
public override void classFixed(int year, int month, string companyName, string unit, int staffNumber)
{
GET_Data.Clear();
GET_Data = getCalender.calendarData(year, month, 1);
if (!Directory.Exists(folderPath + @"\" + unit))
Directory.CreateDirectory(folderPath + @"\" + unit);
// 判斷檔案是否存在
if (fileExist(unit, year, month))
{
fileInfo.Delete();
fileInfo = new FileInfo(filePath);
}
EXCEL_PACKAGE = new ExcelPackage(fileInfo);
WORKSHEET = EXCEL_PACKAGE.Workbook.Worksheets.Add(getCalender.calendarData(year, month, 1)[0].month + "月");
// 寫入公司名稱
WORKSHEET.Cells[companyNameYear, 1].Value = GET_Data[0].year + "年" + GET_Data[0].month + "月 " + companyName + " (" + "單位: " + unit + ")";
WORKSHEET.Cells[companyNameYear, 1, 1, GET_Data.Count + 3].Merge = true;
// 寫入部分(單位)
WORKSHEET.Cells[positionNumber, 1].Value = unit;
WORKSHEET.Cells[positionNumber, 1, positionNumber, GET_Data.Count + 3].Merge = true;
// 表格上色
TABLE_COLOR(positionNumber, 1, System.Drawing.Color.Yellow);
foreach (var dayweekData in GET_Data)
{
WORKSHEET.Cells[dayExcelNuber, 1].Value = "員工";
// 天與週次
WORKSHEET.Cells[dayExcelNuber, Int32.Parse(dayweekData.day) + 1].Value = dayweekData.week + dayweekData.day;
// 休假
WORKSHEET.Cells[dayExcelNuber, Int32.Parse(dayweekData.day) + 2].Value = "休假";
// 表格上色
TABLE_COLOR(dayExcelNuber, GET_Data.Count + 2, System.Drawing.Color.Red);
//加班
WORKSHEET.Cells[dayExcelNuber, Int32.Parse(dayweekData.day) + 3].Value = "加班";
// 輸入員工姓名
for (int x = 0; x < staffNumber; x++)
WORKSHEET.Cells[dataExcelNum + x, 1].Value = "輸入姓名";
// 自動調整距離
WORKSHEET.Cells[WORKSHEET.Dimension.Address].AutoFitColumns();
}
}
// excel save
public override void excelSave(int staffNumber)
{
EXCEL_STYLE(1, 1, staffNumber + 3, GET_Data.Count + 3);
EXCEL_PACKAGE.Save();
}
// 判斷檔案
bool fileExist(string unit, int year, int month)
{
filePath = folderPath + @"\" + unit + @"\" + year.ToString() + "年" + month.ToString() + "月" + ".xlsx";
fileInfo = new FileInfo(filePath);
bool fileExist = fileInfo.Exists;
return fileExist;
}
// 表格顏色
public static void TABLE_COLOR(int fromRow, int fromCol, System.Drawing.Color color)
{
WORKSHEET.Cells[fromRow, fromCol].Style.Fill.PatternType = OfficeOpenXml.Style.ExcelFillStyle.Solid;
WORKSHEET.Cells[fromRow, fromCol].Style.Fill.BackgroundColor.SetColor(color);
}
public static void EXCEL_STYLE(int fromRow, int fromCol, int toRow, int toCol)
{
// 字形
WORKSHEET.Cells.Style.Font.Name = "標楷體";
// 文字大小
WORKSHEET.Cells.Style.Font.Size = 14;
// 水平置中
WORKSHEET.Cells.Style.HorizontalAlignment = OfficeOpenXml.Style.ExcelHorizontalAlignment.Center;
// 垂直中
WORKSHEET.Cells.Style.VerticalAlignment = OfficeOpenXml.Style.ExcelVerticalAlignment.Center;
// 表格框線
WORKSHEET.Cells[fromRow, fromCol, toRow, toCol].Style.Border.Bottom.Style = OfficeOpenXml.Style.ExcelBorderStyle.Medium;
WORKSHEET.Cells[fromRow, fromCol, toRow, toCol].Style.Border.Top.Style = OfficeOpenXml.Style.ExcelBorderStyle.Medium;
WORKSHEET.Cells[fromRow, fromCol, toRow, toCol].Style.Border.Left.Style = OfficeOpenXml.Style.ExcelBorderStyle.Medium;
WORKSHEET.Cells[fromRow, fromCol, toRow, toCol].Style.Border.Right.Style = OfficeOpenXml.Style.ExcelBorderStyle.Medium;
}
}
FormulaClass 腳本
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using OfficeOpenXml;
public class FormulaClass : ExcelClass.ClassInterface.Formula
{
// 檔案類別
FileInfo fileInfo;
// Excel 固定格式 (資料寫入縱)
int dataExcelNum = 4;
ExcelClass.ClassInterface.Preset presetClass = new PresetClass();
public override void createClass(int year, int month, string companyName, string unit, int staffNumber, int operAtingHours,
bool dayShift, bool night, bool bigNight)
{
presetClass.classFixed(year, month, companyName, unit, staffNumber);
int checkExcelNum = dataExcelNum + staffNumber;
int classStateNum = 0;
string className1 = "D";
string className2 = "E";
string className3 = "N";
int dayShiftAdd = 0;
int nightAdd = 0;
int bigNightAdd = 0;
// 營業時數
PresetClass.WORKSHEET.Cells[checkExcelNum + 1, 1].Value = "營業時間 ( " + operAtingHours + "小時 )";
PresetClass.TABLE_COLOR(checkExcelNum + 1, 1, System.Drawing.Color.Red);
PresetClass.WORKSHEET.Cells[checkExcelNum + 1, 1].AutoFitColumns(50);
// 狀態1: 3班都選擇(三班制)
if (dayShift == true && night == true && bigNight == true)
{
// 白班
for (int x = 2; x <= 6; x++)
{
classState(checkExcelNum, x, toggleMessage(dayShift, className1 + classTable(dayShiftAdd)), dayShiftAdd);
dayShiftAdd += 1;
}
// 小夜班(晚班)
for (int x = 7; x <= 11; x++)
{
classState(checkExcelNum, x, toggleMessage(night, className2 + classTable(nightAdd)), nightAdd);
nightAdd += 1;
}
// 大夜班(夜班)
for (int x = 12; x <= 16; x++)
{
classState(checkExcelNum, x, toggleMessage(bigNight, className3 + classTable(bigNightAdd)), bigNightAdd);
bigNightAdd += 1;
}
classStateNum = 1;
}
// 狀態2: 2班都選擇 - 白班,晚班(二班制)
if (dayShift == true && bigNight == true && night != true)
{
// 白班
for (int x = 2; x <= 6; x++)
{
classState(checkExcelNum, x, toggleMessage(dayShift, className1 + classTable(dayShiftAdd)), dayShiftAdd);
dayShiftAdd += 1;
}
// 大夜班(夜班)
for (int x = 7; x <= 11; x++)
{
classState(checkExcelNum, x, toggleMessage(bigNight, className3 + classTable(bigNightAdd)), bigNightAdd);
bigNightAdd += 1;
}
classStateNum = 2;
}
if (dayShift == true && night != true && bigNight != true)
{
// 白班
for (int x = 2; x <= 6; x++)
{
classState(checkExcelNum, x, toggleMessage(dayShift, className1 + classTable(dayShiftAdd)), dayShiftAdd);
dayShiftAdd += 1;
}
classStateNum = 3;
}
// 加班公式
overtimeFormula(checkExcelNum, classStateNum, staffNumber, operAtingHours);
}
public override void excelSave(int satffNumber)
{
PresetClass.EXCEL_STYLE(1, 1, satffNumber + 3, PresetClass.GET_Data.Count + 3);
PresetClass.EXCEL_PACKAGE.Save();
}
// 加班公式
void overtimeFormula(int checkExcelNum, int classStateNum, int staffNumber, int operAtingHours)
{
// 橫向
for (int x = 0; x < staffNumber; x++)
{
// 加班
PresetClass.WORKSHEET.Cells[dataExcelNum + x, PresetClass.GET_Data.Count + 3].Formula = formulaHorizontal(1, dataExcelNum + x, 2);
PresetClass.WORKSHEET.Cells[dataExcelNum + x, PresetClass.GET_Data.Count + 2].Formula = formulaHorizontal(2, dataExcelNum + x, 2);
}
for (int x = 0; x < PresetClass.GET_Data.Count; x++)
{
// 3班
if (classStateNum == 1)
{
// 檢查營業時間
PresetClass.WORKSHEET.Cells[checkExcelNum + 1, x + 2].Formula = formula_vertical_openBusiness(checkExcelNum, x + 2, classStateNum);
var cont = PresetClass.WORKSHEET.ConditionalFormatting.AddGreaterThan(PresetClass.WORKSHEET.Cells[checkExcelNum + 1, x + 2]);
cont.Style.Font.Color.Color = System.Drawing.Color.Red;
cont.Formula = operAtingHours.ToString();
int dayShiftTable = 0;
int nightTable = 0;
int bigNightTable = 0;
// D班含加班
for (int i = 2; i <= 6; i++)
{
PresetClass.WORKSHEET.Cells[checkExcelNum + i, x + 2].Formula = formulaVerticalClassName(dataExcelNum, x + 2, "\"D"
+ classTable(dayShiftTable) + "\"", "\"D" + classTable(dayShiftTable) + "\"", staffNumber);
dayShiftTable += 1;
}
// E班含加班
for (int i = 7; i <= 11; i++)
{
PresetClass.WORKSHEET.Cells[checkExcelNum + i, x + 2].Formula = formulaVerticalClassName(dataExcelNum, x + 2, "\"E"
+ classTable(nightTable) + "\"", "\"E" + classTable(nightTable) + "\"", staffNumber);
nightTable += 1;
}
// N班含加班
for (int i = 12; i <= 16; i++)
{
PresetClass.WORKSHEET.Cells[checkExcelNum + i, x + 2].Formula = formulaVerticalClassName(dataExcelNum, x + 2, "\"N"
+ classTable(bigNightTable) + "\"", "\"N" + classTable(bigNightTable) + "\"", staffNumber);
bigNightTable += 1;
}
// 畫線
PresetClass.EXCEL_STYLE(checkExcelNum + 1, 1, checkExcelNum + 16, PresetClass.GET_Data.Count + 1);
}
// 2班
if (classStateNum == 2)
{
// 檢查營業時間
PresetClass.WORKSHEET.Cells[checkExcelNum + 1, x + 2].Formula = formula_vertical_openBusiness(checkExcelNum, x + 2, classStateNum);
var cont = PresetClass.WORKSHEET.ConditionalFormatting.AddGreaterThan(PresetClass.WORKSHEET.Cells[checkExcelNum + 1, x + 2]);
cont.Style.Font.Color.Color = System.Drawing.Color.Red;
cont.Formula = operAtingHours.ToString();
int dayShiftTable = 0;
int bigNightTable = 0;
// D班含加班
for (int i = 2; i <= 6; i++)
{
PresetClass.WORKSHEET.Cells[checkExcelNum + i, x + 2].Formula = formulaVerticalClassName(dataExcelNum, x + 2, "\"D"
+ classTable(dayShiftTable) + "\"", "\"D" + classTable(dayShiftTable) + "\"", staffNumber);
dayShiftTable += 1;
}
// N班含加班
for (int i = 7; i <= 11; i++)
{
PresetClass.WORKSHEET.Cells[checkExcelNum + i, x + 2].Formula = formulaVerticalClassName(dataExcelNum, x + 2, "\"N"
+ classTable(bigNightTable) + "\"", "\"N" + classTable(bigNightTable) + "\"", staffNumber);
bigNightTable += 1;
}
// 畫線
PresetClass.EXCEL_STYLE(checkExcelNum + 1, 1, checkExcelNum + 11, PresetClass.GET_Data.Count + 1);
}
if (classStateNum == 3)
{
// 檢查營業時間
PresetClass.WORKSHEET.Cells[checkExcelNum + 1, x + 2].Formula = formula_vertical_openBusiness(checkExcelNum, x + 2, classStateNum);
var cont = PresetClass.WORKSHEET.ConditionalFormatting.AddGreaterThan(PresetClass.WORKSHEET.Cells[checkExcelNum + 1, x + 2]);
cont.Style.Font.Color.Color = System.Drawing.Color.Red;
cont.Formula = operAtingHours.ToString();
int dayShiftTable = 0;
// D班含加班
for (int i = 2; i <= 6; i++)
{
PresetClass.WORKSHEET.Cells[checkExcelNum + i, x + 2].Formula = formulaVerticalClassName(dataExcelNum, x + 2, "\"D"
+ classTable(dayShiftTable) + "\"", "\"D" + classTable(dayShiftTable) + "\"", staffNumber);
dayShiftTable += 1;
}
// 畫線
PresetClass.EXCEL_STYLE(checkExcelNum + 1, 1, checkExcelNum + 6, PresetClass.GET_Data.Count + 1);
}
}
}
// 公式 Excel 縱向(班別名稱)
string formula_vertical_openBusiness(int rol, int col, int classState)
{
string result = "";
string dayShiftResult = "";
string nightResult = "";
string bigNightResult = "";
int dayShiftNum = 8;
int nightNum = 8;
int bigNightNum = 8;
int dayShiftNum1 = 12;
int bigNightNum1 = 12;
// 3班制
if (classState == 1)
{
// 白班
for (int x = 2; x <= 6; x++)
{
dayShiftResult += "IF(" + PresetClass.WORKSHEET.Cells[rol + x, col] + "<> 0, " + dayShiftNum + ",0) +";
dayShiftNum += 1;
}
// 小夜(晚班)
for (int x = 7; x <= 11; x++)
{
nightResult += "IF(" + PresetClass.WORKSHEET.Cells[rol + x, col] + "<> 0, " + nightNum + ",0) +";
nightNum += 1;
}
// 大夜(夜班)
for (int x = 12; x <= 16; x++)
{
bigNightResult += "IF(" + PresetClass.WORKSHEET.Cells[rol + x, col] + "<> 0," + bigNightNum + " ,0) +";
bigNightNum += 1;
}
result = dayShiftResult + nightResult + bigNightResult;
}
// 兩班制
if (classState == 2)
{
// 白班
for (int x = 2; x <= 6; x++)
{
dayShiftResult += "IF(" + PresetClass.WORKSHEET.Cells[rol + x, col] + "<> 0," + dayShiftNum1 + " ,0) +";
dayShiftNum1 += 1;
}
// 大夜(夜班)
for (int x = 7; x <= 11; x++)
{
bigNightResult += "IF(" + PresetClass.WORKSHEET.Cells[rol + x, col] + "<> 0," + bigNightNum1 + " ,0) +";
bigNightNum1 += 1;
}
result = dayShiftResult + bigNightResult;
}
// 一班制
if (classState == 3)
{
// 白斑
for (int x = 2; x <= 6; x++)
{
dayShiftResult += "IF(" + PresetClass.WORKSHEET.Cells[rol + x, col] + "<> 0," + dayShiftNum + ",0) +";
dayShiftNum += 1;
}
result = dayShiftResult;
}
result = result.Remove(result.LastIndexOf("+"), 1);
return result;
}
// 公式: Excel 縱向(班別名稱)
string formulaVerticalClassName(int rol, int col, string className, string className2, int staffNumber)
{
string result = "";
for (int x = 0; x < staffNumber; x++)
{
string str = "IF(OR(" + PresetClass.WORKSHEET.Cells[rol + x, col] + "=" + className + ", +"
+ PresetClass.WORKSHEET.Cells[rol + x, col] + "=" + className2 + "), 1, 0) + ";
result += str;
}
result = result.Remove(result.LastIndexOf("+"), 1);
return result;
}
// 公式: Excel 橫向
string formulaHorizontal(int state, int rol, int col)
{
string result = "";
// 加班狀態
for (int x = 0; x < PresetClass.GET_Data.Count; x++)
{
if (state == 1)
{
string str = "IF(OR(" + PresetClass.WORKSHEET.Cells[rol, col + x] + "= \"D1\"," + PresetClass.WORKSHEET.Cells[rol, col + x] + "= \"E1\","
+ PresetClass.WORKSHEET.Cells[rol, col + x] + "= \"N1\"), 1) + IF(OR(" + PresetClass.WORKSHEET.Cells[rol, col + x] + "= \"D2\","
+ PresetClass.WORKSHEET.Cells[rol, col + x] + "= \"E2\"," + PresetClass.WORKSHEET.Cells[rol, col + x] + "= \"N2\"), 2) + IF(OR("
+ PresetClass.WORKSHEET.Cells[rol, col + x] + "= \"D3\"," + PresetClass.WORKSHEET.Cells[rol, col + x] + "= \"E3\","
+ PresetClass.WORKSHEET.Cells[rol, col + x] + "= \"N3\"), 3) + IF(OR(" + PresetClass.WORKSHEET.Cells[rol, col + x] + "= \"D4\","
+ PresetClass.WORKSHEET.Cells[rol, col + x] + "= \"E4\"," + PresetClass.WORKSHEET.Cells[rol, col + x] + "= \"N4\"), 4) +";
result += str;
}
// 假別
if (state == 2)
{
string str = "IF(OR(" + PresetClass.WORKSHEET.Cells[rol, col + x] + "=\"休\"," + PresetClass.WORKSHEET.Cells[rol, col + x] + "=\"特\","
+ PresetClass.WORKSHEET.Cells[rol, col + x] + "=\"病\", " + PresetClass.WORKSHEET.Cells[rol, col + x] + "=\"公\","
+ PresetClass.WORKSHEET.Cells[rol, col + x] + "=\"喪\"," + PresetClass.WORKSHEET.Cells[rol, col + x] + "=\"婚\","
+ PresetClass.WORKSHEET.Cells[rol, col + x] + "=\"公病\"," + PresetClass.WORKSHEET.Cells[rol, col + x] + "=\"事\","
+ PresetClass.WORKSHEET.Cells[rol, col + x] + "=\"災\",),1)+";
result += str;
}
}
result = result.Remove(result.LastIndexOf("+"), 1);
return result;
}
// 班別區分顏色
void classState(int checkExcelNum, int frequency, string message, int addClassTable)
{
PresetClass.WORKSHEET.Cells[checkExcelNum + frequency, 1].Value = message;
if (addClassTable.Equals(0))
PresetClass.TABLE_COLOR(checkExcelNum + frequency, 1, System.Drawing.Color.Orange);
else
PresetClass.TABLE_COLOR(checkExcelNum + frequency, 1, System.Drawing.Color.Yellow);
}
// 加班訊息表示
string classTable(int table)
{
return (table.Equals(0)) ? "" : table.ToString();
}
// toggle 布林為訊息
string toggleMessage(bool toggleBool, string message)
{
return (toggleBool == true) ? message : "";
}
}
----------------
專案設定 :
※ 匯入Dll 檔案,非Unity內部功能,會導致錯誤,因此要更改。
Unity 2019 LTS - 2020 做法:
之前文章 簡易的程式碼,之後要管理會變成相當複雜,因此修改了設計方式,之後比較好維護跟操作。
Calendar 腳本(參數管理):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Calendar
{
private string year; // 年
private string month; // 月
private string days; // 日
GetCalenderData getCalenderData;
public Calendar(string year, string month, string day, GetCalenderData getCalenderData)
{
this.year = year;
this.month = month;
this.days = day;
this.getCalenderData = getCalenderData;
}
public List<Data_data> getData()
{
return getCalenderData.getData(int.Parse(year), int.Parse(month), int.Parse(days));
}
public int dayNumber()
{
return getCalenderData.dayNumber(int.Parse(year), int.Parse(month), int.Parse(days));
}
}
public interface GetCalenderData
{
List<Data_data> getData(int year, int month, int day); // 取得行事曆資料
int dayNumber(int year, int month, int day);
}
// 暫存用
public class Data_data
{
public string year;
public string month;
public string day;
public string week;
public Data_data(string year, string month, string day, string week)
{
this.year = year;
this.month = month;
this.day = day;
this.week = week;
}
}
InterfaceCalender 腳本(方法撰寫):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
public class InterfaceCalender : GetCalenderData
{
DateTime dateTime;
public int dayNumber(int year, int month, int day)
{
int dayNum = 0;
for (int x = 1; x <= DateTime.DaysInMonth(dateTime.Year, dateTime.Month); x++)
{
dayNum = x;
}
return dayNum;
}
// 取得行事曆資料
public List<Data_data> getData(int year, int month, int day)
{
List<Data_data> classExcels = new List<Data_data>();
for(int i = 1; i <= DateTime.DaysInMonth(dateTime.Year, dateTime.Month); i++)
{
dateTime = new DateTime(year, month, i);
classExcels.Add(new Data_data(yearConversion(dateTime.Year).ToString(), dateTime.Month.ToString(), i.ToString(),
weekEngToChin(dateTime.DayOfWeek.ToString())));
}
return classExcels;
}
// 西元轉民國(公式)
int yearConversion(int inVid)
{
return inVid - 1911;
}
// 英文轉中文(星期)
string weekEngToChin(string dayOfWeek)
{
string chinWeek = "";
if (dayOfWeek.ToString().Equals("Monday"))
chinWeek = "一";
if (dayOfWeek.ToString().Equals("Tuesday"))
chinWeek = "二";
if (dayOfWeek.ToString().Equals("Wednesday"))
chinWeek = "三";
if (dayOfWeek.ToString().Equals("Thursday"))
chinWeek = "四";
if (dayOfWeek.ToString().Equals("Friday"))
chinWeek = "五";
if (dayOfWeek.ToString().Equals("Saturday"))
chinWeek = "六";
if (dayOfWeek.ToString().Equals("Sunday"))
chinWeek = "日";
return chinWeek;
}
}
TestRun 腳本(執行):
public class TestRun : MonoBehaviour{
Calendar calendar;
void Start()
{
calendar = new Calendar("2021", "12", "1", new InterfaceCalender());
}
void Update()
{
foreach(var str in calendar.getData())
{
Debug.Log(str.month +"月" + str.day + "日" + "星期:" + str.week);
}
}
}
結果圖:
文字跑馬燈,所先要了解,怎麼顯示模式。
大致上作法 可以用兩種方式來使用。
1. 時間方式計算
2.數字累加
本文採取第二種數字累加的方式來呈現。
邏輯: 數字: 1 代表1個字,表示說 數字累加的方式來顯示文字數量就好了。
本文都採取使用C# 方式來進行編排,不太用到Unity設計界面來操作設定,
但是要先建立需要使用的遊戲物件即可。此篇介面採取比例 (16:9)
使用的遊戲物件:
完成結果後
腳本名稱: InterfaceObj
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
// 介面
public class InterfaceObj
{
// image Obj
public Image imageObj(Image image, Color color , float pointX, float pointY, float sizeX, float sizeY)
{
image.transform.position = new Vector2(Screen.width / 2 * pointX, Screen.height / 2 * pointY);
image.rectTransform.sizeDelta = new Vector2(Screen.width / 2 * sizeX, Screen.height / 2 * sizeY);
image.color = color;
return image;
}
// text Obj
public Text textObj(Text text, string message, FontStyle fontStyle, Color color ,int textSize, float pointX, float pointY, float sizeX, float sizeY)
{
text.text = message;
text.fontStyle = fontStyle;
text.color = color;
text.fontSize = textSize;
text.transform.position = new Vector2(Screen.width / 2 * pointX, Screen.height / 2 * pointY);
text.rectTransform.sizeDelta = new Vector2(Screen.width / 2 * sizeX, Screen.height / 2 * sizeY);
return text;
}
}
// 介面效果類別
public class interfaceAfterEffects
{
/*--------------------------
* 文字效果
--------------------------*/
private float marquee_startTime = 0; // 跑馬燈開始時間
// 文字跑馬燈
public void textEffects(TextEffects effects, Text text, string message, float speed, bool start)
{
text.text = text_Marquee(message, speed, start);
}
// 跑馬燈
private string text_Marquee(string message, float speed, bool start)
{
return message.Substring(0, (int)text_marquee_timeData(message.Length, speed, start));
}
// 計數器(跑馬燈顯示)
private float text_marquee_timeData(float textSize, float speed, bool start)
{
float time = (marquee_startTime < textSize && start.Equals(true)) ? marquee_startTime += speed :
(start.Equals(false)) ? marquee_startTime = 0 : marquee_startTime = textSize;
return time;
}
}
腳本名稱: UI_Layout
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class UI_Layout : MonoBehaviour
{
InterfaceObj interfaceObj = new InterfaceObj();
// 文字訊息(系統)
public void system_mesage_text(Text cameraModel_Text, Text recording_Text, Text InternalLab_Text, Text section8_Text, Text scanning_Text, Text activated, float pointX, float pointY, float sizeX, float sizeY)
{
interfaceObj.textObj(cameraModel_Text, "CAMERA 05s", FontStyle.Normal, new Color(0, 0.9f, 1, 1), (Screen.width / 2) / 10, 0.43f, 1.7f, 0.66f, 0.21f);
interfaceObj.textObj(recording_Text, "REC.", FontStyle.Normal, new Color(1, 1, 1, 1), (Screen.width / 2) / 16, 0.19f, 1.4f, 0.15f, 0.13f);
interfaceObj.textObj(InternalLab_Text, "INTERNAL LAB 13", FontStyle.Normal, new Color(1, 1, 1, 1), (Screen.width / 2) / 27, 0.55f, 1.44f, 0.32f, 0.1f);
interfaceObj.textObj(section8_Text, "SECTION 8 - LEVEL 06", FontStyle.Normal, new Color(1, 1, 1, 1), (Screen.width / 2) / 29, 0.58f, 1.33f, 0.38f, 0.08f);
interfaceObj.textObj(scanning_Text, "SCANNING", FontStyle.Normal, new Color(1, 1, 1, 1), (Screen.width / 2) / 12, 0.34f, 0.85f, 0.47f, 0.18f);
interfaceObj.textObj(activated, "SYSTEM ACTIVATED", FontStyle.Normal, new Color(1, 1, 1, 1), (Screen.width / 2) / 18, 0.4f, 0.7f, 0.6f, 0.12f);
}
}
腳本名稱: Layout_Run
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Layout_Run : UI_Layout
{
public Text cameraModel_Text;
public Text recording_Text;
public Text internalLab_Text;
public Text section8_Text;
public Text scanning_Text;
public Text activated_Text;
// 跑馬燈
interfaceAfterEffects scanning_Text_marquess = new interfaceAfterEffects();
interfaceAfterEffects activated_Text_marquess = new interfaceAfterEffects();
public bool sw = false;
private void Update()
{
systemLayoutMessge();
if (Input.GetKeyDown(KeyCode.W))
sw = !sw;
}
private void systemLayoutMessge()
{
system_mesage_text(cameraModel_Text, recording_Text, internalLab_Text, section8_Text, scanning_Text, activated_Text, pointX, pointY, sizeX, sizeY);
// 文字跑馬燈
scanning_Text_marquess.textEffects(scanning_Text, "SCANNING", 0.6f, sw);
activated_Text_marquess.textEffects(activated_Text, "SYSTEM ACTIVATED", 0.4f, sw);
}
}
Unity 資料寫入MySql做法,大致上兩種做法,一種是透過PHP方式來當媒介方式,進行寫入。
另一種則是透過MySQL Community提供Dll 方式直接寫入。
此篇介紹就是以MySQL Community提供Dll方式來進行實踐,但是會有一些版本的問題存在,此篇 Unity 2018版本
MySQL Community 可到這個網站下載,但是要測試可以用的版本~
為了方面實踐,以下我提供兩個載點:
載點2 提取码:nunc (也可以掃以下二維碼下載)
再來就是我們要下載 Xampp了,之記得安裝正確。
安裝結束後,打開軟體會呈現這個畫面
再點Admin開啟Mysql 網頁版,建立資料庫與資料表,可以查看Mysql 指令方式來建立。
Mysql 指令許多網站都有整理出來: 凍仁大大整理的筆記之分享(MySql 指令)~~
再此 感謝整理Mysql指令大大的辛苦。
建立資料庫與資料表後,呈現以下圖片 (此篇只建立 ID與姓名)
再來就是程式部分了。
本篇習慣介面物件,都以程式方式來撰寫,比較不會到介面下,進行操作調整。
ScriptName: InterfaceObj 介面物件設定
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Events;
public class InterfaceObj : MonoBehaviour
{
// 輸入介面
public InputField inputField(InputField inputField, float pointX, float pointY, float sizeX, float sizeY)
{
inputField.transform.position = new Vector2(Screen.width / 2 * pointX, Screen.height / 2 * pointY);
inputField.image.rectTransform.sizeDelta = new Vector2(Screen.width / 2 * sizeX, Screen.height / 2 * sizeY);
return inputField;
}
// 文字顯示
public Text text(Text text, string message, float pointX, float pointY, float sizeX, float sizeY)
{
text.transform.position = new Vector2(Screen.width / 2 * pointX, Screen.height / 2 * pointY);
text.rectTransform.sizeDelta = new Vector2(Screen.width/2 * sizeX, Screen.height /2 * sizeY);
text.text = message;
text.fontSize = 16;
text.fontStyle = FontStyle.Normal;
text.color = Color.black;
return text;
}
// 按鈕
public Button button(Button button, string btnMessage, UnityAction onClick, float pointX, float pointY, float sizeX, float sizeY)
{
button.transform.position = new Vector2(Screen.width / 2 * pointX, Screen.height / 2 * pointY);
button.image.rectTransform.sizeDelta = new Vector2(Screen.width / 2 * sizeX, Screen.height / 2 * sizeY);
Text buttonName = button.transform.GetChild(0).GetComponent<Text>();
buttonName.name = "buttonName";
buttonName.text = btnMessage;
button.onClick.AddListener(onClick);
return button;
}
}
程式注意:
using UnityEngine.Events; 要記得匯入,不然按鈕無法進行探聽事件。
UnityAction onClick
button.onClick.AddListener(onClick);
-------------------------------
Script Name: Layout 介面執行
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Layout : InterfaceObj
{
// 介面物件
public Button enter_btn;
public InputField name_Input;
public Text name_Text;
public Text disPlayMessage;
// 輸入數值
private string name;
// 查詢介面物件
public InputField inquireName_Input;
public Text inquireName_Text;
public Button inquireEnter_Button;
public Text inquireDisPlayerMessage;
// 查詢名稱
private string inquireName;
// 資料表名稱
private string tableName = "member";
public float pointX, pointY;
private void Start()
{
text(name_Text, "姓名", 0.16f, 1.78f, 0.1f, 0.1f);
button(enter_btn, "確定", enter_OnClick, 0.79f, 1.8f, 0.3f, 0.15f);
// 查詢
text(inquireName_Text, "姓名", 0.16f, 1.23f, 0.1f, 0.1f);
button(inquireEnter_Button, "查詢", inquireEnter_OnClick, 0.8f, 1.24f, 0.3f, 0.15f);
}
private void Update()
{
name = inputField(name_Input, 0.43f, 1.8f, 0.4f, 0.15f).text;
// 判斷是否輸入
if (name_Input.text == "")
enter_btn.interactable = false;
else enter_btn.interactable = true;
text(disPlayMessage, "連接資料庫訊息: " + MySqlClass.result + ", 寫入訊息: " + MySqlClass.disPlayerRead, 1f, 1.55f,1.5f, 0.1f);
// 查詢
inquireName = inputField(inquireName_Input, 0.43f, 1.24f, 0.4f, 0.15f).text;
if (inquireName_Input.text == "")
inquireEnter_Button.interactable = false;
else inquireEnter_Button.interactable = true;
text(inquireDisPlayerMessage, "連接資料庫訊息: " + MySqlClass.result + ", 查詢訊息: " + MySqlClass.disPlayInquire, 1f, 1f, 1.5f, 0.1f);
}
// 查詢
void inquireEnter_OnClick()
{
openDataBase();
MySqlClass.INQUIRE_TABLE(tableName, inquireName);
MySqlClass.closeSqlConnection();
}
void enter_OnClick()
{
openDataBase();
MySqlClass.WriteTable(tableName, name);
MySqlClass.closeSqlConnection();
}
private void OnApplicationQuit()
{
MySqlClass.closeSqlConnection();
}
void openDataBase()
{
string connectionString = string.Format("Server = {0}; Database = {1}; UserID = {2}; Password = {3};", MySqlClass.hostType, MySqlClass.dataBaseType, MySqlClass.idType, MySqlClass.passWordTpye);
MySqlClass.openSqlConnection(connectionString);
MySqlClass.myObjType = MySqlClass.GetDataSet(connectionString);
}
}
程式注意:
OnApplicationQuit(); 關閉應用程式的,主要關閉Mysql 連接,如果有寫執行續(Thread)也需要用到。
------------------------------------
Script Name: MySqlClass 執行資料庫動作
using System.Collections.Generic;
using System.Data;
using System;
using MySql.Data.MySqlClient;
using System.Text;
using UnityEngine;
public class MySqlClass
{
// Just like MyConn.conn in Story Tolls before
public static MySqlConnection dbConnection;
// DataBase Ip
private static string host = "127.0.0.1";
public static string hostType
{
set { host = value; }
get { return host; }
}
// DataBase user Id
private static string id = "gsp40214";
public static string idType
{
set { id = value; }
get { return id; }
}
// DataBase Password
private static string passWord = "123456";
public static string passWordTpye
{
set { passWord = value; }
get { return passWord; }
}
// DataBase Name
private static string dataBase = "pixnet_test";
public static string dataBaseType
{
set { dataBase = value; }
get { return dataBase; }
}
// 連線結果
public static string result = "";
// 寫入結果
public static string disPlayerRead = "";
// 查詢結果
public static string disPlayInquire = "";
// DataSet
private static DataSet myObj;
public static DataSet myObjType
{
set { myObj = value; }
get { return myObj; }
}
// Connect to database
public static void openSqlConnection(string connectionString)
{
dbConnection = new MySqlConnection(connectionString);
dbConnection.Open();
result = dbConnection.ServerVersion;
}
// closeSql
public static void closeSqlConnection()
{
dbConnection.Close();
dbConnection = null;
}
// MySQL Query
public static void doQuery(string sqlQuery)
{
IDbCommand dbCommand = dbConnection.CreateCommand();
dbCommand.CommandText = sqlQuery;
IDataReader reader = dbCommand.ExecuteReader();
reader.Close();
reader = null;
dbCommand.Dispose();
dbCommand = null;
}
#region Get DataSet
public static DataSet GetDataSet(string sqlString)
{
DataSet ds = new DataSet();
try
{
MySqlDataAdapter da = new MySqlDataAdapter(sqlString, dbConnection);
}
catch (Exception e)
{
throw new Exception("SQL:" + sqlString + "\n");
e.Message.ToString();
}
return ds;
}
#endregion
// 轉utf8
public static string messageToUtf8(string message)
{
UTF8Encoding encoder = new UTF8Encoding();
byte[] bytes = Encoding.UTF8.GetBytes(message);
string utf8ReturnString = encoder.GetString(bytes);
return utf8ReturnString;
}
static string ID;
static string Message;
// 寫入資料表
public static void WriteTable(string DataBaseTable, string message)
{
MySqlCommand command = dbConnection.CreateCommand();
string sqlText = "select count(1) from " + DataBaseTable + " where name=('" + message + "')";
MySqlCommand cmd1 = new MySqlCommand(sqlText, dbConnection);
int count = (int)(long)cmd1.ExecuteScalar();
if (count > 0)
{
Debug.Log("已經有資料囉");
disPlayerRead = "已經有資料囉";
}
else
{
Message = messageToUtf8(message);
command.CommandText = "Insert into " + DataBaseTable + "(id, name) value('" + ID + "','" + Message + "')";
command.ExecuteNonQuery();
disPlayerRead = "載入資料成功";
Debug.Log("載入資料成功");
}
}
// 資料庫查詢
public static void INQUIRE_TABLE(string dataBaseTitle, string message)
{
string sqlText = "select * from " + dataBaseTitle + " where name='" + message + "'";
MySqlCommand cmd = new MySqlCommand(sqlText, dbConnection);
MySqlDataReader data = cmd.ExecuteReader();
while (data.Read())
{
try
{
disPlayInquire = "資料庫裡面有資料: " + data[1];
}
catch (Exception e)
{
data.Close();
closeSqlConnection();
}
}
data.Close();
}
}
程式注意:
"Insert into " + DataBaseTable + "(id, name) value('" + ID + "','" + Message + "')"; 資料庫寫入語法
"select * from " + dataBaseTitle + " where name='" + message + "'"; 資料庫查詢語法
data[1]; 裡面數字代表資料表欄位編號。
如果MySQL有增加新的使用者記得設定權限,否則也會連接失敗。
----------------------------------
發佈後 執行結果:
寫入資料庫,記得Mysql 網頁重新整理~
如果我們要查詢資料庫是否有小花結果:
如果發現到發佈之後,無法寫入資料庫,但是在Unity軟體上執行正常,表示缺少Dll 檔案。
缺少檔案: 可以到Unity安裝路徑下:
Unity → Editor → Data → Mono → lib → mono → 2.0
I18N的相關檔案複製到Unity專案目錄下貼上試試。
上一篇 有提到Unity 寫入Excel 基本功能,延續上一篇的方法來實踐有趣的事情吧。
這次 我們會使用Unity UI 功能,來方便設計基本的介面設定。
此篇由程式方式來設定UI設定,比較方便,不用到版面慢慢設定的麻煩。
首先 我們要先知道,我們介面需要使用甚麼物件來進行。
此篇使用物件: Text, InputField, Button
因此建立 UI Class 來方式來撰寫物件設定的功能
Script Name: UIClass
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Events;
public class UIClass : MonoBehaviour
{
public InputField inputField(InputField obj, float pointX, float pointY, float sizeX, float sizeY){
obj.transform.position = new Vector2(Screen.width / 2 * pointX, Screen.height /2 * pointY);
obj.image.rectTransform.sizeDelta = new Vector2(Screen.width / 2 * sizeX, Screen.height / 2 * sizeY);
return obj;
}
public Text text(Text text, string message, float pointX, float pointY, float sizeX, float sizeY, Color color, int textSize, FontStyle fontStyle){
text.transform.position = new Vector2(Screen.width /2 * pointX, Screen.height /2 * pointY);
text.rectTransform.sizeDelta = new Vector2(Screen.width / 2 * sizeX, Screen.height / 2 * sizeY);
text.text = message;
text.fontSize = textSize;
text.fontStyle = fontStyle;
text.color = color;
return text;
}
public Button button(Button button, string button_Name, float pointX, float pointY, float sizeX, float sizeY, UnityAction onClick){
button.transform.position = new Vector2(Screen.width / 2 * pointX, Screen.height /2 * pointY);
button.image.rectTransform.sizeDelta = new Vector2(Screen.width / 2 * sizeX, Screen.height / 2 * sizeY);
GameObject buttonName = button.transform.GetChild(0).gameObject;
buttonName.name = "button_Name";
buttonName.GetComponent<Text>().text = button_Name;
button.onClick.AddListener(onClick);
return button;
}
}
程式部分:
1. UnityAction - (記得匯入) using UnityEngine.Events;
※ 由於Unity UI Button 是兩個物件: button與Text物件形成父子物件,為了好分辨,用程式直接進行修改,就不用針對一個一個物件慢慢修改。
2. GameObject buttonName = button.transform.GetChild(0).gameObject;
-------------------------------------------------------------------------------
Script Name : Layout
using UnityEngine;
using UnityEngine.UI;
using System;
public class Layout : UIClass
{
public InputField year_inputField, month_inputField, company_inputField;
public Text year_text, month_text, company_text;
public Button enter_btn, quit_btn;
ClassSchedule classSchedule;
private void Start() {
button_function();
}
private void Update() {
layout_();
}
// 按鈕
void button_function(){
// 按鈕: 建立班表與應用程式離開
button(enter_btn, "建立班表格式", 1.3f, 0.3f, 0.3f, 0.2f, Enter_Onclick);
button(quit_btn, "結束應用程式", 1.7f, 0.3f, 0.3f, 0.2f, Quit_Onclick);
}
// 介面
void layout_(){
// 年
inputField(year_inputField, 0.3f, 1.7f, 0.3f, 0.2f);
text(year_text, "年", 0.5f, 1.7f, 0.05f, 0.07f, Color.white, 16, FontStyle.Normal);
// 月
inputField(month_inputField, 0.9f, 1.7f, 0.3f, 0.2f);
text(month_text, "月", 1.1f, 1.7f, 0.05f, 0.07f, Color.white, 16, FontStyle.Normal);
// 公司名稱
inputField(company_inputField, 1.65f, 1.7f, 0.3f, 0.2f);
text(company_text, "公司名稱", 1.36f, 1.7f, 0.14f, 0.07f, Color.white, 16, FontStyle.Normal);
}
void Enter_Onclick(){
classSchedule = new ClassSchedule(Int32.Parse(year_inputField.text), Int32.Parse(month_inputField.text), company_inputField.text, "行政");
classSchedule.classShedule_Fixed();
}
void Quit_Onclick(){
Application.Quit();
}
}
程式部分:
1. classSchedule = new ClassSchedule(Int32.Parse(year_inputField.text), Int32.Parse(month_inputField.text), company_inputField.text, "行政");
※ 也可以直接進行ClassSchedule 指定參數,不建議使用。
2. button(enter_btn, "建立班表格式", 1.3f, 0.3f, 0.3f, 0.2f, Enter_Onclick); - Enter_Onclick : 直接寫成方法來探聽即可。
-------------------------------------------------------------------------------
Script Name : classShedule_Fixed
using System;
using System.IO;
using OfficeOpenXml;
using Drawing = System.Drawing;
public class ClassSchedule
{
private int _year, _month;
private string _companyName, _unit;
public ClassSchedule(int year, int month, string companyName, string unit)
{
this._year = year;
this._month = month;
this._companyName = companyName;
this._unit = unit;
}
// 建立資料位置
private string folderPath = @"D:\班表\";
// 檔案路徑
private string filePath;
// Excel 固定格式
int companyNameYear = 1; // 年
int positionNumber = 2; // 職位
int dayExcelNuber = 3; // 天
// excel 組件
ExcelPackage excelPackage;
ExcelWorksheet workSheets;
// 每年每月類別
QueryMonthAndDays queryMonthAndDays;
// 檔案類別
FileInfo fileInfo;
public void classShedule_Fixed(){
// 建立資料夾與檔案位置
createExcelFile();
queryMonthAndDays = new QueryMonthAndDays(_year, _month, 1);
excelPackage = new ExcelPackage(fileInfo);
workSheets = excelPackage.Workbook.Worksheets.Add(queryMonthAndDays.getData()[0].month + "月");
// 寫入公司名稱
workSheets.Cells[companyNameYear, 1].Value = queryMonthAndDays.getData()[0].year + "年" + queryMonthAndDays.getData()[0].month + "月 " + _companyName + " (" + "單位: " + _unit + ")";
workSheets.Cells[companyNameYear, 1, 1, queryMonthAndDays.getData().Count + 3].Merge = true;
// 寫入單位
workSheets.Cells[positionNumber, 1].Value = _unit;
workSheets.Cells[positionNumber, 1, positionNumber, queryMonthAndDays.getData().Count + 3].Merge = true;
tableColor(positionNumber, 1, Drawing.Color.Yellow);
// 寫入年月與公司名稱
foreach (var dayweekData in queryMonthAndDays.getData())
{
workSheets.Cells[dayExcelNuber, 1].Value = "員工";
// 天與週次
workSheets.Cells[dayExcelNuber, Int32.Parse(dayweekData.day) + 1].Value = dayweekData.week + dayweekData.day;
// 休假
workSheets.Cells[dayExcelNuber, Int32.Parse(dayweekData.day) + 2].Value = "休假";
// 表格上色
tableColor(dayExcelNuber, queryMonthAndDays.getData().Count + 2, Drawing.Color.Red);
//加班
workSheets.Cells[dayExcelNuber, Int32.Parse(dayweekData.day) + 3].Value = "加班";
}
excelStyle(companyNameYear, 1, dayExcelNuber, queryMonthAndDays.getData().Count + 3);
excelPackage.Save();
}
// excel 樣式
void excelStyle(int fromRow, int fromCol, int toRow, int toCol)
{
// 字形
workSheets.Cells.Style.Font.Name = "標楷體";
// 文字大小
workSheets.Cells.Style.Font.Size = 16;
// 水平置中
workSheets.Cells.Style.HorizontalAlignment = OfficeOpenXml.Style.ExcelHorizontalAlignment.Center;
// 垂直中
workSheets.Cells.Style.VerticalAlignment = OfficeOpenXml.Style.ExcelVerticalAlignment.Center;
// 表格框線
workSheets.Cells[fromRow, fromCol, toRow, toCol].Style.Border.Bottom.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thick;
workSheets.Cells[fromRow, fromCol, toRow, toCol].Style.Border.Top.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin;
workSheets.Cells[fromRow, fromCol, toRow, toCol].Style.Border.Left.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin;
workSheets.Cells[fromRow, fromCol, toRow, toCol].Style.Border.Right.Style = OfficeOpenXml.Style.ExcelBorderStyle.Thin;
}
// Drawing
void tableColor(int fromRow, int fromCol, Drawing.Color color)
{
workSheets.Cells[fromRow, fromCol].Style.Fill.PatternType = OfficeOpenXml.Style.ExcelFillStyle.Solid;
workSheets.Cells[fromRow, fromCol].Style.Fill.BackgroundColor.SetColor(color);
}
// 建立檔案
private void createExcelFile()
{
// 建立資料夾
if (!Directory.Exists(folderPath + @"\" + _unit))
Directory.CreateDirectory(folderPath + @"\" + _unit);
// 判斷檔案是否存在
if (fileExist())
{
fileInfo.Delete();
fileInfo = new FileInfo(filePath);
}
}
// 檔案是否存在
bool fileExist()
{
filePath = folderPath + @"\" + _unit + @"\" + _year.ToString() + "年" + _month.ToString() + "月" + ".xlsx";
fileInfo = new FileInfo(filePath);
bool fileExist = fileInfo.Exists;
return fileExist;
}
}
程式部分:
1. using Drawing = System.Drawing - 記得Unity 專案匯入System.Drawing.dll 才能執行
2. excelPackage = new ExcelPackage(fileInfo) - 檔案位置
3. workSheets = excelPackage.Workbook.Worksheets.Add(queryMonthAndDays.getData()[0].month + "月") - Excel 活頁簿
4. excelStyle(companyNameYear, 1, dayExcelNuber, queryMonthAndDays.getData().Count + 3) - excelStyle(X位置, Y軸位置, X幾格, Y幾格)
5. excelPackage.Save(); - Excel 寫入後存檔
-------------------------------------------------------------------------------
Unity 介面部分
GameObjcet :
那些物件不用設定任何事情,只需要改名及把Layout 腳本放到Canvas物件中。
Layout 腳本顯示Public 物件名稱放入場景物件
再來調整製作過程需要的解析度,本版主習慣用 16:9 的比例來做專案,除非遊戲企劃規定,依據更改。
※ 順便一提: 此篇沒有UI 介面設計,單位輸入的,如果使用者需要可以自行加入。
以上設定好後,就可以試試了,以下執行結果。
Unity 底下操作寫入Excel 檔案且匯出xlsx 副檔名,並且需要前置工作,否則匯入dll檔案會導致Unity 錯誤,如果少了dll 檔案發佈專案後,也會導致無法正常輸入Excel 檔案。
當然在正常遊戲設計領域,很少會使用到這項功能,但是 Unity 強項,並非只有在製作遊戲上面哦。
以下來進行,今天主題的內容如何實踐吧~
所先要下載,可以製作Excel方式來進行方便開發。
Excel 全部相關DLL下載 提取碼: 0xso / 備用載點
所使用的工具 EPPLUS 組件方式,來方便撰寫。
Dll 載入Unity中,會出現一些紅字問題。
如下圖:
那個問題是Unity 表示,無法讀取 .Net Standard 2.0 API, 因此需要設定專案。
操作方式為: File/Build Setting....(快捷鍵 : ctrl +shift +b),會出現 Bulid setting 視窗
再設定 API Compatibility Level 選項選擇 .Net4.0 (文章Unity 版本: 2018)
選擇完後,錯誤紅字就會消失,如果還有出現錯誤請刪除 System.Data.dll 這個檔案即可。
再來 我們就來測試一下是否,可以正常寫入與讀取匯出。
我查詢了許多相關資訊,也是有很多方式,覺得比較複雜,這裡提供簡單方式來進行。
再來就是程式部分了 呵呵~
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Excel;
using OfficeOpenXml;
using System.IO;
public class ExcelTest : MonoBehaviour
{
public string filePath = @"C:\Users\user\Desktop\123.xlsx";
FileInfo fileInfo;
ExcelPackage package;
ExcelWorksheet worksheet;
string str = "";
private void OnGUI() {
// 寫入Excel 檔案裏面內容
if(GUILayout.Button("寫入Excel 檔案內容"))
writeExcel();
if(GUILayout.Button("讀取 Excel 檔案名稱")){
str = readExcel();
}
GUILayout.Label("測試: " + str);
}
// 讀取 Excel 檔案
string readExcel(){
string result = "";
fileInfo = new FileInfo(filePath);
package = new ExcelPackage(fileInfo);
worksheet = package.Workbook.Worksheets["Test"];
string str = worksheet.Cells[1, 1].Text;
string str1 = worksheet.Cells[2, 1].Text;
string str2 = worksheet.Cells[3, 1].Text;
result = str + str1 + str2;
return result;
}
// 寫入Excel 檔案
void writeExcel(){
fileInfo = new FileInfo(filePath);
if(fileInfo.Exists){
fileInfo.Delete();
fileInfo = new FileInfo(filePath);
}
fileInfo = new FileInfo(filePath);
package = new ExcelPackage(fileInfo);
worksheet = package.Workbook.Worksheets.Add("Test");
worksheet.Cells[1, 1].Value = "ID";
worksheet.Cells[2, 1].Value = "小名";
worksheet.Cells[3, 1].Value = "小花";
package.Save();
}
}
程式部分解釋:
Excel 裡面表格,可以看成 X, Y 座標的方式來進行。
string str = worksheet.Cells[1, 1].Text;
以上那段程式解釋 string 負責接值由worksheet.cells.text 來傳值。
會發現到Cells[1, 1] 裡面顯示1,1 那個表示Excel 表格位置,當然也可以直接寫成Cells[A1],只是不建議就是了。
主要是寫成A1的話,是直接指定,如果多筆資料要一個一個指定較麻煩,不如使用迴圈條件來進行寫入較方便。
這段程式: worksheet = package.Workbook.Worksheets["Test"]; 是Excel 活頁簿 ,因此Excel 必要有活頁簿才能進行執行。
----------------
結果圖如下:
Unity 發佈與未發佈,執行結果是會有差距的。
因此需要發佈執行為主。
Unity 讀取年月日,並顯並不是難事,只要使用 System.DateTime 函式庫就可以了。
顯示設定當下月份天數,比較少用到這個功能,除非班表功能(讓使用者)才會使用的上。
那我們趕快進入程式碼部分吧
這段程式: resultDisPlayer(int year, int month, int day) 為了方便使用者開發,
可以透過UI介面或者Unity GUI (過去式介面) 來進行溝通。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
public class TestScript : MonoBehaviour
{
DateTime startdate;
// Start is called before the first frame update
void Start()
{
Debug.Log(resultDisPlayer(2021, 5, 1));
}
string resultDisPlayer(int year, int month, int day){
string result = "";
startdate = new DateTime(year, month, day);
for (int x = day; x <= DateTime.DaysInMonth(startdate.Year, startdate.Month); x++)
{
startdate = new DateTime(startdate.Year, startdate.Month, x);
string str = "民國: " + vidToRepublic(startdate.Year) + "年 "
+ startdate.Month + "月 " + x + "日 " + "星期: " + weekEngToChin(startdate.DayOfWeek.ToString());
result += str + "\n";
}
return result;
}
// 西元轉換民國 (公式)
string vidToRepublic(int dayofYear)
{
int vidNumber = 1911; // 公式
int sum = dayofYear - vidNumber;
return sum.ToString();
}
// 英文轉中文(星期)
string weekEngToChin(string dayOfWeek)
{
string chinWeek = "";
if (dayOfWeek.ToString().Equals("Monday"))
chinWeek = "星期一";
if (dayOfWeek.ToString().Equals("Tuesday"))
chinWeek = "星期二";
if (dayOfWeek.ToString().Equals("Wednesday"))
chinWeek = "星期三";
if (dayOfWeek.ToString().Equals("Thursday"))
chinWeek = "星期四";
if (dayOfWeek.ToString().Equals("Friday"))
chinWeek = "星期五";
if (dayOfWeek.ToString().Equals("Saturday"))
chinWeek = "星期六";
if (dayOfWeek.ToString().Equals("Sunday"))
chinWeek = "星期日";
return chinWeek;
}
}
顯示結果如下:
目前 很夯 Visual Studio Code 來撰寫程式,主要是開啟速度較快與程式撰寫者,可自行匯入模組,那我們如何進行用 Visual Studio Code 來撰寫Unity底下程式。
如步驟以下:
官方連結: Visual Studio Code
下載完成後_介面如下:
Unity開發,這裡使用 C#語言來撰寫,因此下載 .NET Core SDK 工具來協助方便撰寫。
官方連結: .NET Core SDK
再到此圖紅色框框點選下載
出現以下圖片結果表示成功了
若沒辦法顯示,請進行重新開機即可。
程式碼來源: https://blog.csdn.net/BIGMAD/article/details/71698310
---- 以下是編輯路徑腳本 ----
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EditorPathScript : MonoBehaviour {
public Color rayColor = Color.red;
public List path_objs = new List();
Transform[] theArray;
void OnDrawGizmos(){
Gizmos.color = rayColor;
theArray = GetComponentsInChildren();
path_objs.Clear();
foreach(Transform path_obj in theArray){
if(path_obj != this.transform)
path_objs.Add(path_obj);
}
for(int i = 0;i0){
Vector3 previous = path_objs[i-1].position;
Gizmos.DrawLine(previous,position);
Gizmos.DrawWireSphere(position, 0.3f);
}
}
}
}
---- 以下是物件跟隨行為腳本 ----
using UnityEngine;
using System.Collections;
public class FollowPath : MonoBehaviour {
public bool StartFollow = false;
public EditorPathScript PathToFollow;
public int CurrentWayPointID = 0;
public float Speed;//移动速度
public float reachDistance = 0f;//里路径点的最大范围
public string PathName;//跟随路径的名字
private string LastName;
private bool ChangePath = true;
void Start () {
}
void Update () {
if (!StartFollow)
return;
if (ChangePath)
{
PathToFollow = GameObject.Find(PathName).GetComponent();
ChangePath = false;
}
if (LastName != PathName)
{
ChangePath = true;
}
LastName = PathName;
float distance = Vector3.Distance(PathToFollow.path_objs[CurrentWayPointID].position, transform.position);
//transform.Translate(PathToFollow.path_objs[CurrentWayPointID].position * Time.deltaTime * Speed, Space.Self);
transform.position = Vector3.MoveTowards(transform.position, PathToFollow.path_objs[CurrentWayPointID].position, Time.deltaTime * Speed);
if (distance <= reachDistance)
{
CurrentWayPointID++;
}
if (CurrentWayPointID >= PathToFollow.path_objs.Count)
{
CurrentWayPointID = 0;
}
}
}
此文章內容則是以Unity來製作Android系統讓手機產生震動的效果。
---------------
Script (C#):
using System.Collections;
using UnityEngine;
public static class Vibration
{
#if UNITY_ANDROID && !UNITY_EDITOR
public static AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
public static AndroidJavaObject currentActivity = unityPlayer.GetStatic("currentActivity");
public static AndroidJavaObject vibrator = currentActivity.Call("getSystemService", "vibrator");
#else
public static AndroidJavaClass unityPlayer;
public static AndroidJavaObject currentActivity;
public static AndroidJavaObject vibrator;
#endif
private static bool isAndroid()
{
#if UNITY_ANDROID && UNITY_EDTOIR
return true;
#else
return false;
#endif
}
///
/// 震動
///
public static void Vibrate()
{
if (isAndroid())
vibrator.Call("vibrate");
else
Handheld.Vibrate();
}
///
/// 震動(毫秒)
///
///
public static void Vibrate(long milliseconds)
{
if (isAndroid())
vibrator.Call("vibrate", milliseconds);
else
Handheld.Vibrate();
}
///
/// 模式震動
/// 重覆
///
///
///
public static void Vibrate(long[] pattern, int repeat)
{
if (isAndroid())
vibrator.Call("vibrate", pattern, repeat);
else
Handheld.Vibrate();
}
///
/// 判斷是否震動器
///
///
public static bool HasVibrator()
{
return isAndroid();
}
///
/// 取消
///
public static void Cancel()
{
if (isAndroid())
vibrator.Call("cancel");
}
}
此文章內容則是...遊戲設計很常用到的第一人稱,雖然Unity官方也很套件可以使用。
但是自己研究後,發現到滿有趣的,有興趣的玩家,不妨也來試試吧。
此功能有使用到Unity Editor 功能哦,請嘗試玩家注意。
詳細資料在於Google Blogger 部落格這裡,請點選
以下則是這次內容FPS 程式範例:
FPS (第一人稱) 腳本
mpleSmoothMouseLook (攝影機) 腳本
---------
Script (C#):
using UnityEngine;
using System.Collections;
using UnityEditor;
[RequireComponent(typeof(Animator))]
[RequireComponent(typeof(Rigidbody))]
[RequireComponent(typeof(CapsuleCollider))]
public class FPS : MonoBehaviour {
[MenuItem("PlayerMode/FPS")]
static void Fps() {
GameObject fps = new GameObject("FPS");
fps.transform.position = Vector3.zero;
// FPS add component
fps.AddComponent();
GameObject cam = new GameObject("Camera");
cam.transform.parent = fps.transform;
// Camera add component
cam.AddComponent();
cam.AddComponent();
cam.AddComponent();
cam.AddComponent();
cam.AddComponent();
}
[SerializeField]
float speed = 10.0f, gravity = 10.0f, maxVelocityChange = 10f, jumpHeight = 2.0f;
[SerializeField]
bool canJump = true;
private bool grounded = false;
Rigidbody rigidbody;
CapsuleCollider playerCollider;
void Awake(){
rigidbody = GetComponent();
rigidbody.freezeRotation = true;
rigidbody.useGravity = false;
gameObject.AddComponent();
}
void FixedUpdate() {
if (grounded) {
// Calculate how fast we should be moving
Vector3 targetVelocity = new Vector3(Input.GetAxis("Horizontal") * speed,0, Input.GetAxis("Vertical")* speed);
targetVelocity = transform.TransformDirection(targetVelocity);
// Apply a force that attempts to reach our target velocity
Vector3 velocity = rigidbody.velocity;
Vector3 velocityChange = (targetVelocity - velocity);
velocityChange.x = Mathf.Clamp(velocityChange.x, -maxVelocityChange, maxVelocityChange);
velocityChange.z = Mathf.Clamp(velocityChange.z, -maxVelocityChange, maxVelocityChange);
velocityChange.y = 0;
rigidbody.AddForce(velocityChange, ForceMode.VelocityChange);
//Jump
if (canJump && Input.GetButton("Jump"))
rigidbody.velocity = new Vector3(velocity.x, CalculateJumpVerticalSpeed(), velocity.z);
}
rigidbody.AddForce(new Vector3(0,-gravity * rigidbody.mass, 0));
grounded = false;
}
void OnCollisionStay()
{
grounded = true;
}
float CalculateJumpVerticalSpeed() {
return Mathf.Sqrt(2 * jumpHeight * gravity);
}
}
------------
Script (C#):
using UnityEngine;
using System.Collections;
[AddComponentMenu("Camera/Simple Smooth Mouse Look ")]
public class SimpleSmoothMouseLook : MonoBehaviour {
Vector2 _mouseAbsolute;
Vector2 _smoothMouse;
public Vector2 clampInDegrees = new Vector2(360, 180);
public bool lockCursor;
public Vector2 sensitivity = new Vector2(2, 2);
public Vector2 smoothing = new Vector2(3, 3);
public Vector2 targetDirection;
public Vector2 targetCharacterDirection;
// Assign this if there's a parent object controlling motion, such as a Character Controller.
// Yaw rotation will affect this object instead of the camera if set.
public GameObject characterBody;
void Start()
{
// Set target direction to the camera's initial orientation.
targetDirection = transform.localRotation.eulerAngles;
// Set target direction for the character body to its inital state.
if (characterBody) targetCharacterDirection = characterBody.transform.localRotation.eulerAngles;
}
void Update()
{
// Ensure the cursor is always locked when set
Screen.lockCursor = lockCursor;
// Allow the script to clamp based on a desired target value.
var targetOrientation = Quaternion.Euler(targetDirection);
var targetCharacterOrientation = Quaternion.Euler(targetCharacterDirection);
// Get raw mouse input for a cleaner reading on more sensitive mice.
var mouseDelta = new Vector2(Input.GetAxisRaw("Mouse X"), Input.GetAxisRaw("Mouse Y"));
// Scale input against the sensitivity setting and multiply that against the smoothing value.
mouseDelta = Vector2.Scale(mouseDelta, new Vector2(sensitivity.x * smoothing.x, sensitivity.y * smoothing.y));
// Interpolate mouse movement over time to apply smoothing delta.
_smoothMouse.x = Mathf.Lerp(_smoothMouse.x, mouseDelta.x, 1f / smoothing.x);
_smoothMouse.y = Mathf.Lerp(_smoothMouse.y, mouseDelta.y, 1f / smoothing.y);
// Find the absolute mouse movement value from point zero.
_mouseAbsolute += _smoothMouse;
// Clamp and apply the local x value first, so as not to be affected by world transforms.
if (clampInDegrees.x < 360)
_mouseAbsolute.x = Mathf.Clamp(_mouseAbsolute.x, -clampInDegrees.x * 0.5f, clampInDegrees.x * 0.5f);
var xRotation = Quaternion.AngleAxis(-_mouseAbsolute.y, targetOrientation * Vector3.right);
transform.localRotation = xRotation;
// Then clamp and apply the global y value.
if (clampInDegrees.y < 360)
_mouseAbsolute.y = Mathf.Clamp(_mouseAbsolute.y, -clampInDegrees.y * 0.5f, clampInDegrees.y * 0.5f);
transform.localRotation *= targetOrientation;
// If there's a character body that acts as a parent to the camera
if (characterBody)
{
var yRotation = Quaternion.AngleAxis(_mouseAbsolute.x, characterBody.transform.up);
characterBody.transform.localRotation = yRotation;
characterBody.transform.localRotation *= targetCharacterOrientation;
}
else
{
var yRotation = Quaternion.AngleAxis(_mouseAbsolute.x, transform.InverseTransformDirection(Vector3.up));
transform.localRotation *= yRotation;
}
}
}
此文章內容是天氣裡面的龍捲風物理哦。
龍捲風作法,我相信大家都應該知道 可以用粒子系統 (Particle System)及加上旋轉就能形成。
我們馬上來看以下程式碼之範例吧...
--------------------------
Script (C#):
using UnityEngine;
using System.Collections;
public class Tornado_Main : MonoBehaviour {
[Tooltip("This controls the radius of your tornados pull range")]
public float radius = 65.28f;
public float maxRadiusToPullIn = 10;
[Tooltip("NEGATIVE NUMBERS ONLY. This pulls objects into the tornado")]
public float PullingInPower = -70;
public float MaxPullingIn = 20;
Collider[] colliders;
void OnDrawGizmos()
{
Gizmos.DrawWireSphere(transform.position, radius);
}
void Update()
{
colliders = Physics.OverlapSphere(transform.position, radius);
foreach (Collider c in colliders)
{
if (c.GetComponent() == null)
{
continue;
}
Ray ray = new Ray(transform.position, c.transform.position - transform.position);
RaycastHit hit;
Physics.Raycast(ray, out hit);
if (hit.collider.name != c.gameObject.name || hit.distance < MaxPullingIn)
{
continue;
}
else
{
Rigidbody rigidbody = c.GetComponent();
rigidbody.AddExplosionForce(PullingInPower, transform.position, radius);
}
}
}
}
---------------
Script(C#):
using UnityEngine;
using System.Collections;
public class Outer_Tornado : MonoBehaviour
{
public float radius = 19.48f;
public float outsideSpeed = 0.7f;
public float maxPullInLength = 24.96f;
public float power = 1;
Collider[] colliders;
void OnDrawGizmos()
{
Gizmos.DrawWireSphere(transform.position, radius);
}
void Update ()
{
transform.RotateAround(transform.parent.transform.position, Vector3.up, outsideSpeed);
colliders = Physics.OverlapSphere(transform.position, radius);
foreach (Collider c in colliders)
{
if (c.GetComponent() == null)
{
continue;
}
Rigidbody rigidbody = c.GetComponent();
Ray ray = new Ray(transform.position, c.transform.position - transform.position);
RaycastHit hit;
Physics.Raycast(ray, out hit);
if (hit.distance > maxPullInLength)
{
continue;
}
if (c.transform.position.z > 8.5)
{
Vector3 Force = new Vector3(transform.position.x - c.transform.position.x, rigidbody.velocity.y / 2, transform.position.z - c.transform.position.z) * power;
rigidbody.AddForceAtPosition(Force, transform.position);
}
rigidbody.AddForceAtPosition((transform.position - c.transform.position) * power, transform.position);
}
}
}
---------------
結果圖:
成功的話,可以像以下圖片一樣,讓物件飛起來哦....
此文章內容則是物件跟隨,遊戲設計當中,可是很常見的功能。
以下則是此文章範例程式:
--------------
Script (c#):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PetFlow : MonoBehaviour {
public Transform player;
public float maxDis = 5;
private SmoothFollowerObj posFollow; // 控制位置平滑移動
private SmoothFollowerObj lookFollow; // 控制朝向平滑轉換
public Vector3 posistionVector; // 角色位置移動,方向向量
public Vector3 lookVector; //角色朝向變化,朝向向量
private Vector3 lastVelocityDir; // 上一次移動方向
private Vector3 lastPost; // 之前移動目標位置
void Start()
{
posFollow = new SmoothFollowerObj(0.5f, 0.5f);
lookFollow = new SmoothFollowerObj(0.1f, 0.1f);
posFollow.Update(transform.position, 0, true); // 初始位置
lookFollow.Update(player.transform.position, 0, true);
posistionVector = new Vector3(0, 0.5f, 1.7f);
lookVector = new Vector3(0, 0, 1.5f);
lastVelocityDir = player.transform.forward;
lastPost = player.transform.position;
}
void Update()
{
float dis = Vector3.Distance(transform.position, player.position);
if (dis > maxDis)
PetMoveFlow();
}
// 物件移動
void PetMoveFlow()
{
lastVelocityDir += (player.position - lastPost) * 5;
lastPost = player.position;
lastVelocityDir += player.transform.forward * Time.deltaTime;
lastVelocityDir = lastVelocityDir.normalized;
Vector3 horizontal = transform.position - player.position;
Vector3 horizontal2 = horizontal;
Vector3 vertical = player.up;
Vector3.OrthoNormalize(ref vertical, ref horizontal); // https://docs.unity3d.com/ScriptReference/Vector3.OrthoNormalize.html api
// https://docs.unity3d.com/ScriptReference/Vector3-sqrMagnitude.html api
if (horizontal.sqrMagnitude > horizontal2.sqrMagnitude)
horizontal = horizontal2;
transform.position = posFollow.Update(player.position + horizontal * Mathf.Abs(posistionVector.z)
+ vertical * posistionVector.y, Time.deltaTime);
horizontal = lastVelocityDir;
Vector3 look = lookFollow.Update(player.position + horizontal * lookVector.z
- vertical * lookVector.y, Time.deltaTime);
// https://docs.unity3d.com/ScriptReference/Quaternion.FromToRotation.html api
transform.rotation = Quaternion.FromToRotation(transform.forward, look - transform.position) * transform.rotation;
}
}
class SmoothFollowerObj
{
private Vector3 targetPosistion; // 目標位置
private Vector3 position; // 位置
private Vector3 velocity; // 速率
private float smoothingTime; // 平滑時間
private float prediction; // 預測
public SmoothFollowerObj(float smoothingTime)
{
targetPosistion = Vector3.zero;
position = Vector3.zero;
this.smoothingTime = smoothingTime;
prediction = 1;
}
public SmoothFollowerObj(float smoothingTime, float prediction)
{
targetPosistion = Vector3.zero;
position = Vector3.zero;
this.smoothingTime = smoothingTime;
this.velocity = velocity;
}
// 更新位置資訊
public Vector3 Update(Vector3 targetPosistionNew, float dataTime)
{
Vector3 targetvelocity = (targetPosistionNew - targetPosistion) / dataTime; // 獲取目標移動的方向向量
targetPosistion = targetPosistionNew;
float d = Mathf.Min(1, dataTime / smoothingTime);
velocity = velocity * (1 - d) + (targetPosistion + targetvelocity * prediction - position) * d;
position += velocity * Time.deltaTime;
return position;
}
// 根據傳輸進來數據, 重置本地參數
public Vector3 Update(Vector3 targetPosistionNew, float dataTime, bool rest)
{
if (rest)
{
targetPosistion = targetPosistionNew;
position = targetPosistionNew;
velocity = Vector3.zero;
return position;
}
return Update(targetPosistionNew, dataTime);
}
public Vector3 GetPosistion() { return position; }
public Vector3 GetVelocity() { return velocity; }
}
---------------
成功的話....物件會會跟著所指定的位置來做移動哦。
此外...可以利用物件與物件關係(親代)來調整中心點位置。
已經好久沒來「痞客邦」來寫關於Unity 技術文章,都在Goolge Blooger 那邊撰寫比較多
回歸主題...此文章內容是Unity裡面Sprite 裡面的功能。
不多說...直接看程式碼之範例吧
忘了一件事情,此程式碼操作使用到Unity Editor 外掛編輯元件哦。
-----------------
Unity Edtor (C#):
using UnityEngine;
using System.Collections;
using UnityEditor;
using System.Collections.Generic;
public class SpriteProcessor : EditorWindow
{
private Vector2 vector2Label; // 標籤位置(BeginScrollView 用的)
private GUIStyle style = new GUIStyle(); // GUI 風格
private string spriteSegmentationType = ""; // Sprite 分割像素
private string spriteName = ""; // Sprite 分格後名稱
private int spriteTitleNumber = 0; // 分格後的名稱編號
private int spriteSegmentationNumber; // 分割數值字串轉型
// 呼叫視窗
[MenuItem(測試/Sprite寫法)]
public static void spriteWindowSetting()
{
SpriteProcessor windows = EditorWindow.GetWindow(typeof(SpriteProcessor), true, "Sprite 自動分割") as SpriteProcessor;
windows.Show();
}
// 選擇貼圖(Sprite)
static Object[] getSelectTexture()
{
return Selection.GetFiltered(typeof(Texture2D), SelectionMode.DeepAssets);
}
void OnGUI()
{
style.fontSize = 18;
style.normal.textColor = Color.white;
GUI.Label(new Rect(0, 0, 100, 30), "圖片分割", style);
// Sprite 分割像素
GUILayout.BeginArea(new Rect(0, 30, 300, 100));
GUILayout.BeginHorizontal();
GUILayout.Label("Sprite 分割像素");
GUILayout.EndHorizontal();
GUILayout.EndArea();
// Sprite 分割像素輸入
GUILayout.BeginArea(new Rect(150, 30, 150, 100));
GUILayout.BeginHorizontal();
spriteSegmentationType = GUILayout.TextField(spriteSegmentationType);
GUILayout.EndHorizontal();
GUILayout.EndArea();
// Sprite標籤
GUILayout.BeginArea(new Rect(0, 110, 300, 100));
GUILayout.BeginHorizontal();
GUILayout.Label("Sprite 標簽名稱");
GUILayout.EndArea();
GUILayout.EndHorizontal();
// Sprite標籤輸入
GUILayout.BeginArea(new Rect(148, 110, 150, 100));
GUILayout.BeginHorizontal();
spriteName = GUILayout.TextField(spriteName);
GUILayout.EndHorizontal();
GUILayout.EndArea();
GUILayout.BeginArea(new Rect(200, 150, 100, 100));
GUILayout.BeginHorizontal();
if (GUILayout.Button("確定"))
SetPivots();
GUILayout.EndHorizontal();
GUILayout.EndArea();
}
// 分割動作
void SetPivots()
{
spriteTitleNumber = 0;
// 判斷轉型成功執行分割
if (int.TryParse(spriteSegmentationType, out spriteSegmentationNumber))
{
Object[] textures = getSelectTexture();
Selection.objects = new Object[0];
SpriteMetaData d = new SpriteMetaData();
foreach (Texture2D texture in textures)
{
string path = AssetDatabase.GetAssetPath(texture);
TextureImporter ti = AssetImporter.GetAtPath(path) as TextureImporter;
ti.isReadable = true;
List newData = new List();
newData.Remove(d);
for (int x = 0; x < texture.width; x += spriteSegmentationNumber)
{
for (int y = 0; y < texture.height; y += spriteSegmentationNumber)
{
d.alignment = 9;
d.pivot = new Vector2(0.5f, 0.5f);
d.name = spriteName + "_" + (spriteTitleNumber += 1);
d.rect = new Rect(x, y, spriteSegmentationNumber, spriteSegmentationNumber);
newData.Add(d);
}
}
ti.spritesheet = newData.ToArray();
AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate);
}
}
}
}
-------------- 顯示結果圖下