离散仿真引擎基础及Unity3D入门编程实践

一、简答题:离散仿真引擎基础

  • 解释 游戏对象(GameObjects) 和 资源(Assets)的区别与联系。

区别:
游戏对象(GameObjects):相当于一个容器,可以容纳组件。它们本身不做任何事情,需要特殊属性(special properties)才能成为一个角色、一种环境或者一种特殊效果
资源(Assets):在项目中可以导入使用的文件,包括图像、视频、脚本文件、预制文件等
联系:
游戏对象可以通过资源保存起来
资源可以用来创建对象实例
一个资源可以创建多个对象

  • 下载几个游戏案例,分别总结资源、对象组织的结构(指资源的目录组织结构与游戏对象树的层次结构)

总结:

资源的目录组织结构:

Assets:主文件夹,包含所有工程需要用到的资源

Editor:所有在Editor和它的子文件夹的脚本,都不会作为运行期脚本被编译,而是作为动态添加Unity编译器功能的脚本来编译,在该文件夹和其子文件夹的脚本不能被添加到GameObject上

Materials:材料

Models:模型

Prefabs:预设

Scenes:场景

Textures:纹理

Scripts:C#脚本

游戏对象树的层次结构的组织则主要是一个继承或是组合/聚合的关系

  • 编写一个代码,使用 debug 语句来验证 MonoBehaviour 基本行为或事件触发的条件
    • 基本行为包括 Awake() Start() Update() FixedUpdate() LateUpdate()
    • 常用事件包括 OnGUI() OnDisable() OnEnable()

 代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Test : MonoBehaviour
{
    private void Awake()
    {
        Debug.Log("awake\n");
    }
    // Start is called before the first frame update
    void Start()
    {
        Debug.Log("start\n");
    }

    // Update is called once per frame
    void Update()
    {
        Debug.Log("update\n");
    }

    private void FixedUpdate()
    {
        Debug.Log("fixed-update\n");
    }

    private void OnEnable()
    {
        Debug.Log("on-enbale\n");
    }

    private void OnDisable()
    {
        Debug.Log("on-disable\n");
    }
    private void OnGUI()
    {
        Debug.Log("on-GUI\n");
    }
}
  • 查找脚本手册,了解 GameObject,Transform,Component 对象
    • 分别翻译官方对三个对象的描述(Description)

GameObject:Unity场景中所有实体的基类。
Transform:一个对象的位置、旋转角度和大小。它用来存储和控制物体的位置、旋转角度和大小。每一个Transform组件都有一个父Transform组件,这便允许我们分层地应用位置、旋转角度和大小。

Component :基类,用于处理附加到 GameObject 的所有内容。

  • 资源预设(Prefabs)与 对象克隆 (clone)
    • 预设(Prefabs)有什么好处?

预设是一个容易复用的类模板,可以迅速方便创建大量相同属性的对象、操作简单,代码量少,减少出错概率。修改的复杂度降低,一旦需要修改所有相同属性的对象,只需要修改预设即可,所有通过预设实例化的对象都会做出相应变化。

  • 预设与对象克隆 (clone or copy or Instantiate of Unity Object) 关系?

两者都可用于批量产生对象,但是对象克隆不受克隆本体的影响,因此A对象克隆的对象B不会因为A的改变而相应改变。

二、 编程实践:小游戏

        通过井字棋小游戏的案例学习,理解 MVC 元素之间的协作关系,对Unity 3D、IMGUI有了初步的了解后,现在模仿井字棋代码,参考“井字棋”案例,编写制作一个简单的计算器。

(一)效果展示

【实验三 【Unity3D入门IMGUI应用案例】计算器工具开发】 https://www.bilibili.com/video/BV1Zj41147cu/?share_source=copy_web&vd_source=955c67204ef88fcc078f826659e5a767

(二)代码实现

        代码使用IMGUI及OnGui()函数搭建绘制UI界面,程序按注释的代码框架,严格分为 Model-View-Controller三部分,并满足三部分之间的约束关系。

using UnityEngine;
using System.Text.RegularExpressions;
using System;

public class Calculator : MonoBehaviour
{
    // Entities and their states / Model
    public string result = "";//用来显示结果
    public static string str1 = "";//第一个操作数
    public static bool haveDot = false;//第二个操作数
    public static bool isCaclutate = false;

    public static bool IsNumeric(string value)
    {
        return Regex.IsMatch(value, @"^[+-]?\d*[.]?\d*$");
    }


    // View render entities / models
    // Here! you cannot modify model directly, use components/controls to do it
    void OnGUI()
    {
        //对数字进行处理
        if (GUI.Button(new Rect(100, 100, 100, 60), "CE"))
        {
            result = "";
            str1 = "";
            haveDot = false;
        }
        if (GUI.Button(new Rect(210, 100, 100, 60), "×") && str1.Substring(str1.Length - 1, 1) != "-" && str1.Substring(str1.Length - 1, 1) != "+" && str1.Substring(str1.Length - 1, 1) != ".")
        {
            if (IsNumeric(str1.Substring(str1.Length - 1, 1)))
            {
                Debug.Log(str1.Substring(str1.Length - 1, 1));
                str1 += "*";
                haveDot = false;
                isCaclutate = false;
            }
            result = str1;
        }
        if (GUI.Button(new Rect(320, 100, 100, 60), "÷") && str1.Substring(str1.Length - 1, 1) != "-" && str1.Substring(str1.Length - 1, 1) != "+" && str1.Substring(str1.Length - 1, 1) != ".")
        {
            if (IsNumeric(str1.Substring(str1.Length - 1, 1)))
            {
                str1 += "/";
                haveDot = false;
                isCaclutate = false;
            }
            result = str1;
        }
        if (GUI.Button(new Rect(430, 100, 100, 60), "←"))
        {
            if (isCaclutate == true)
            {
                str1 = "";
                isCaclutate = false;
            }
            else if (result.Length != 0)
            {
                str1 = str1.Substring(0, str1.Length - 1);
            }
            result = str1;
        }
        if (GUI.Button(new Rect(100, 170, 100, 60), "1"))
        {
            if (isCaclutate == true)
            {
                str1 = "";
                isCaclutate = false;
                haveDot = false;
            }
            str1 += "1";
            result = str1;
        }
        if (GUI.Button(new Rect(210, 170, 100, 60), "2"))
        {
            if (isCaclutate == true)
            {
                str1 = "";
                isCaclutate = false;
                haveDot = false;
            }
            str1 += "2";
            result = str1;
        }
        if (GUI.Button(new Rect(320, 170, 100, 60), "3"))
        {
            if (isCaclutate == true)
            {
                str1 = "";
                isCaclutate = false;
                haveDot = false;
            }
            str1 += "3";
            result = str1;
        }
        if (GUI.Button(new Rect(430, 170, 100, 60), "-"))
        {
            if (IsNumeric(str1.Substring(str1.Length - 1, 1)) && str1.Substring(str1.Length - 1, 1) != "-" && str1.Substring(str1.Length - 1, 1) != "+" && str1.Substring(str1.Length - 1, 1) != ".")
            {
                str1 += "-";
                haveDot = false;
                isCaclutate = false;
            }
            result = str1;
        }
        if (GUI.Button(new Rect(100, 240, 100, 60), "4"))
        {
            if (isCaclutate == true)
            {
                str1 = "";
                isCaclutate = false;
                haveDot = false;
            }
            str1 += "4";
            result = str1;
        }
        if (GUI.Button(new Rect(210, 240, 100, 60), "5"))
        {
            if (isCaclutate == true)
            {
                str1 = "";
                isCaclutate = false;
                haveDot = false;
            }
            str1 += "5";
            result = str1;
        }
        if (GUI.Button(new Rect(320, 240, 100, 60), "6"))
        {
            if (isCaclutate == true)
            {
                str1 = "";
                isCaclutate = false;
                haveDot = false;
            }
            str1 += "6";
            result = str1;
        }
        if (GUI.Button(new Rect(430, 240, 100, 60), "+") && str1.Substring(str1.Length - 1, 1) != "+" && str1.Substring(str1.Length - 1, 1) != "-" && str1.Substring(str1.Length - 1, 1) != ".")
        {
            if (IsNumeric(str1.Substring(str1.Length - 1, 1)))
            {
                str1 += "+";
                haveDot = false;
                isCaclutate = false;
            }
            result = str1;
        }
        if (GUI.Button(new Rect(100, 310, 100, 60), "7"))
        {
            if (isCaclutate == true)
            {
                str1 = "";
                isCaclutate = false;
                haveDot = false;
            }
            str1 += "7";
            result = str1;
        }
        if (GUI.Button(new Rect(210, 310, 100, 60), "8"))
        {
            if (isCaclutate == true)
            {
                str1 = "";
                isCaclutate = false;
                haveDot = false;
            }
            str1 += "8";
            result = str1;
        }
        if (GUI.Button(new Rect(320, 310, 100, 60), "9"))
        {
            if (isCaclutate == true)
            {
                str1 = "";
                isCaclutate = false;
                haveDot = false;
            }
            str1 += "9";
            result = str1;
        }
        if (GUI.Button(new Rect(430, 310, 100, 130), "="))
        {

            var tmp = Evaluator.Eval(result);
            Debug.Log(tmp.ToString());
            result = tmp.ToString();
            str1 = result;
            isCaclutate = true;
            if (result.Contains("."))
            {
                haveDot = true;
            }
        }
        if (GUI.Button(new Rect(100, 380, 210, 60), "0"))
        {
            if (isCaclutate == true)
            {
                str1 = "";
                isCaclutate = false;
                haveDot = false;
            }
            str1 += "0";
            result = str1;
        }

        if (GUI.Button(new Rect(320, 380, 100, 60), "."))
        {
            if (isCaclutate == true)
            {
                str1 = "0.";
                isCaclutate = false;
            }
            if (IsNumeric(str1.Substring(str1.Length - 1, 1)) && str1.Substring(str1.Length - 1, 1) != "." && haveDot == false)
            {
                Debug.Log(str1.Substring(str1.Length - 1, 1));
                str1 += ".";
                haveDot = true;
                isCaclutate = false;
            }
            result = str1;
        }
        GUI.TextArea(new Rect(100, 20, 430, 60), result);
    }

    // Components
    // here! any ui can not be referenced
    
    /**/
    /// <summary>
    /// 动态求值
    /// </summary>
    public class Evaluator
    {
        /**/
        /// <summary>
        /// 计算结果,如果表达式出错则抛出异常
        /// </summary>
        /// <param name="statement">表达式,如"1+2+3+4"</param>
        /// <returns>结果</returns>
        public static object Eval(string statement)
        {
            if (statement.Trim() != string.Empty)
            {
                Evaluator evaluator = new Evaluator();
                return evaluator.GetFormulaResult(statement);
            }
            else
            {
                return null;
            }
        }


        private object GetFormulaResult(string s)
        {
            if (s == "")
            {
                return null;
            }
            string S = BuildingRPN(s);

            string tmp = "";
            System.Collections.Stack sk = new System.Collections.Stack();

            char c = ' ';
            System.Text.StringBuilder Operand = new System.Text.StringBuilder();
            double x, y;
            for (int i = 0; i < S.Length; i++)
            {
                c = S[i];
                //added c==',' for germany culture
                if (char.IsDigit(c) || c == '.' || c == ',')
                {
                    //数据值收集.
                    Operand.Append(c);
                }
                else if (c == ' ' && Operand.Length > 0)
                {
                    #region 运算数转换
                    try
                    {
                        tmp = Operand.ToString();
                        if (tmp.StartsWith("-"))//负数的转换不被直接支持.
                        {
                            //这个分支可能永远不会被执行.
                            sk.Push(-((double)Convert.ToDouble(tmp.Substring(1, tmp.Length - 1))));
                        }
                        else
                        {
                            sk.Push(Convert.ToDouble(tmp));
                        }
                    }
                    catch
                    {
                        return null; 
                    }
                    Operand = new System.Text.StringBuilder();
                    #endregion
                }
                else if (c == '+'//运算符处理.双目运算处理.
                    || c == '-'
                    || c == '*'
                    || c == '/'
                    || c == '%'
                    || c == '^')
                {
                    #region 双目运算
                    if (sk.Count > 0)//输入的表达式根本没有包含运算符.或是根本就是空串.
                    {
                        y = (double)sk.Pop();
                    }
                    else
                    {
                        sk.Push(0);
                        break;
                    }
                    if (sk.Count > 0)
                        x = (double)sk.Pop();
                    else
                    {
                        sk.Push(y);
                        break;
                    }
                    switch (c)
                    {
                        case '+':
                            sk.Push(x + y);
                            break;
                        case '-':
                            sk.Push(x - y);
                            break;
                        case '*':
                            if (y == 0)
                            {
                                sk.Push(x * 1);
                            }
                            else
                            {
                                sk.Push(x * y);
                            }
                            break;
                        case '/':
                            if (y == 0)
                            {
                                sk.Push(x / 0);
                            }
                            else
                            {
                                sk.Push(x / y);
                            }
                            break;
                        case '%':
                            sk.Push(x % y);
                            break;
                        case '^':
                            if (x > 0)
                            {
                                sk.Push(System.Math.Pow(x, y));
                            }
                            
                            else
                            {
                                
                                double t = y;
                                string ts = "";
                                t = 1 / (2 * t);
                                ts = t.ToString();
                                if (ts.ToUpper().LastIndexOf('E') > 0)//
                                {
                                    ;
                                }
                            }
                            break;
                    }
                    #endregion
                }
                else if (c == '!')//单目取反.
                {
                    sk.Push(-((double)sk.Pop()));
                }
            }
            if (sk.Count > 1)
            {
                return null;
            }
            if (sk.Count == 0)
            {
                return null;
            }
            return sk.Pop();
        }
        /**/
        /// <summary>
        /// 
        /// </summary>
        private string BuildingRPN(string s)
        {
            System.Text.StringBuilder sb = new System.Text.StringBuilder(s);
            System.Collections.Stack sk = new System.Collections.Stack();
            System.Text.StringBuilder re = new System.Text.StringBuilder();

            char c = ' ';
            for (int i = 0; i < sb.Length; i++)
            {
                c = sb[i];
                if (char.IsDigit(c) || c == ',')
                    re.Append(c);
                char.IsLetter(c);
                switch (c)
                {
                    case '+':
                    case '-':
                    case '*':
                    case '/':
                    case '%':
                    case '^':
                    case '!':
                    case '(':
                    case ')':
                    case '.':
                        re.Append(c);
                        break;
                    default:
                        continue;
                }
            }
            sb = new System.Text.StringBuilder(re.ToString());
            #region 对负号进行预转义处理.负号变单目运算符求反.
            for (int i = 0; i < sb.Length - 1; i++)
                if (sb[i] == '-' && (i == 0 || sb[i - 1] == '('))
                    sb[i] = '!';
            //字符转义.
            #endregion
            #region 将中缀表达式变为后缀表达式.
            re = new System.Text.StringBuilder();
            for (int i = 0;
                i < sb.Length;
                i++)
            {
                if (char.IsDigit(sb[i]) || sb[i] == '.')//如果是数值.
                {
                    re.Append(sb[i]);
                    //加入后缀式
                }
                else if (sb[i] == '+'
                    || sb[i] == '-'
                    || sb[i] == '*'
                    || sb[i] == '/'
                    || sb[i] == '%'
                    || sb[i] == '^'
                    || sb[i] == '!')//.
                {
                    #region 运算符处理
                    while (sk.Count > 0) //栈不为空时
                    {
                        c = (char)sk.Pop();
                        //将栈中的操作符弹出.
                        if (c == '(') //如果发现左括号.停.
                        {
                            sk.Push(c);
                            //将弹出的左括号压回.因为还有右括号要和它匹配.
                            break;
                            //中断.
                        }
                        else
                        {
                            if (Power(c) < Power(sb[i]))//如果优先级比上次的高,则压栈.
                            {
                                sk.Push(c);
                                break;
                            }
                            else
                            {
                                re.Append(' ');
                                re.Append(c);
                            }
                            //如果不是左括号,那么将操作符加入后缀式中.
                        }
                    }
                    sk.Push(sb[i]);
                    //把新操作符入栈.
                    re.Append(' ');
                    #endregion
                }
                else if (sb[i] == '(')//基本优先级提升
                {
                    sk.Push('(');
                    re.Append(' ');
                }
                else if (sb[i] == ')')//基本优先级下调
                {
                    while (sk.Count > 0) //栈不为空时
                    {
                        c = (char)sk.Pop();
                        //pop Operator
                        if (c != '(')
                        {
                            re.Append(' ');
                            re.Append(c);
                            //加入空格主要是为了防止不相干的数据相临产生解析错误.
                            re.Append(' ');
                        }
                        else
                            break;
                    }
                }
                else
                    re.Append(sb[i]);
            }
            while (sk.Count > 0)//最后一个弹栈.
            {
                re.Append(' ');
                re.Append(sk.Pop());
            }
            #endregion
            re.Append(' ');
            return FormatSpace(re.ToString());
            //在这里进行一次表达式格式化.这里就是后缀式了.  
        }

        /// <summary>  
        /// 优先级别测试函数.  
        /// </summary>  
        /// <param name="opr"></param>  
        /// <returns></returns>  
        private static int Power(char opr)
        {
            switch (opr)
            {
                case '+':
                case '-':
                    return 1;
                case '*':
                case '/':
                    return 2;
                case '%':
                case '^':
                case '!':
                    return 3;
                default:
                    return 0;
            }
        }

        /// <summary>  
        /// 规范化逆波兰表达式.
        /// </summary>  
        /// <param name="s"></param>  
        /// <returns></returns>  
        private static string FormatSpace(string s)
        {
            System.Text.StringBuilder ret = new System.Text.StringBuilder();
            for (int i = 0; i < s.Length; i++)
            {
                if (!(s.Length > i + 1 && s[i] == ' ' && s[i + 1] == ' '))
                    ret.Append(s[i]);
                else
                    ret.Append(s[i]);
            }
            return ret.ToString();
            //.Replace( '!','-' )
        }
    }
}

(三)组建运行

        创建一个空的模型(Create Empty),同样命名为Calculator,将Calculator.cs拖动到Calculator上面。

        再点击工具栏中的运行按钮即可运行计算器:

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值