Geomystery(幾何迷城)的遊戲引擎設計與實現
阿新 • • 發佈:2017-08-26
isp sum output body ide 信息 orm hid 的人 在這裏介紹Geomystery(幾何迷城)的遊戲引擎設計與實現。
業務邏輯:
引擎采用模塊化的MVC(Model模型,View視圖,Controller控制)設計方式,這樣有助於運用多種設計模式,便於日後的修改與維護。 M模型坐標系中的模型是被操作的對象,模型坐標系是被“顯示坐標系”顯示的單位。 V視圖(顯示坐標系)是模型在用戶屏幕的一個投影,這也和顯卡、顯示器的工作原理有關。 C控制器操作某個邏輯坐標系模型A,或者每次操作後由控制器直接通知視圖(顯示坐標系)刷新模型A的投影a,或者由“監聽器”(監聽者模式)發現模型A被“更新(改變)”,之後通知視圖(顯示坐標系)刷新a。
顯卡和顯示器: WIN2D封裝了DirectX,在屏幕上一個區域(DC)顯示一些靜態內容,或者每隔一段時間(一般是1/60s,即60fps)在屏幕上的一個區域刷新顯示一些內容。所以每次刷新,只需要遍歷V視圖(顯示坐標系),把其中的內容呈現給用戶,這樣用戶畫線時的動態過程就可以被看到,同時用戶改變程序窗口大小(刷新顯示區DC)的操作也不會清空屏幕。
“橫看成嶺側成峰,遠近高低各不同。不識廬山真面目,只緣身在此山中。”廬山只有一座,而在同一時刻,不同的人看廬山,能看到不同的廬山。所以對於一個“邏輯的”坐標系M,可能對應多個“顯示的”坐標系V1V2V3V4,每個顯示坐標系Vn都有自己的“操縱桿”Cn與控制器相連,他們都可以通過自己的操縱桿Cx操作自己看到的(其實是同一個)邏輯坐標系。於是,如果多個用戶同時操作,就可能有沖突,需要使用操作系統學到的同步互斥方法,由於這次生產實習時間有限,所以這裏簡化一下,只有一個控制器,有一個主“屏幕”(視圖顯示坐標系),其他屏幕都是副屏幕,只能觀察,沒有操作功能。一個娃娃機有前後左右四塊玻璃觀察窗,但是只有一套操縱桿。
邏輯坐標系中的元素: 0,幾何元素Geometry。幾何元素有一個坐標系內唯一的編號(id),記錄了自己的投影(們),記錄了自己受誰影響rely,記錄了自己影響誰influence。 幾何元素分為點Point和點集合PointSet。 1,點就是一個邏輯點,關鍵屬性是邏輯坐標XY。 2,點集是一個接口(interface),因為這款遊戲是尺規作圖,所以點集只分線(Line)和圓(Circle) (為什麽是Circle而不是Ellipse,因為圓規只能畫圓,這裏涉及到圓的定義方式,所以只提供圓工具,橢圓工具可以後續添加) 點集接口有一個交點List<Point2> IPointSet.Intersection(IPointSet another)函數需要後續實現 3, 線分為線段射線直線,所以我們需要一個LineType來記錄線的線型
四種操作,“增刪改查”:用戶可能對一個幾何體有定義(創建),移動,變形,刪除等操作,這些操作都會被記錄在操作棧中,這樣用戶可以撤銷重做。查詢操作主要包含在代碼邏輯中,也是必不可少的。
“繁瑣”的“綁定”:對於一個引擎的每一個“零件”,都應該記錄豐富的信息,這種豐富可能是繁瑣多余的,但是會給操作帶來方便。
邏輯坐標系知道有多少用戶在看自己(邏輯坐標系綁定顯示坐標系(們)),顯示坐標系知道自己看的是誰(顯示坐標系綁定邏輯坐標系),邏輯坐標系知道自己裏面有多少邏輯幾何元素,每個邏輯幾何元素知道自己在哪個邏輯坐標系中,視圖坐標系知道自己裏面有多少視圖幾何元素或其他,每個視圖幾何元素知道自己位於哪個視圖坐標系中,每個邏輯幾何元素知道自己有多少個視圖幾何元素,每個視圖幾何元素知道自己是哪個邏輯幾何元素的“投影像”。
引擎采用模塊化的MVC(Model模型,View視圖,Controller控制)設計方式,這樣有助於運用多種設計模式,便於日後的修改與維護。 M模型坐標系中的模型是被操作的對象,模型坐標系是被“顯示坐標系”顯示的單位。 V視圖(顯示坐標系)是模型在用戶屏幕的一個投影,這也和顯卡、顯示器的工作原理有關。 C控制器操作某個邏輯坐標系模型A,或者每次操作後由控制器直接通知視圖(顯示坐標系)刷新模型A的投影a,或者由“監聽器”(監聽者模式)發現模型A被“更新(改變)”,之後通知視圖(顯示坐標系)刷新a。
顯卡和顯示器: WIN2D封裝了DirectX,在屏幕上一個區域(DC)顯示一些靜態內容,或者每隔一段時間(一般是1/60s,即60fps)在屏幕上的一個區域刷新顯示一些內容。所以每次刷新,只需要遍歷V視圖(顯示坐標系),把其中的內容呈現給用戶,這樣用戶畫線時的動態過程就可以被看到,同時用戶改變程序窗口大小(刷新顯示區DC)的操作也不會清空屏幕。
M | 邏輯坐標系,系統坐標系 |
V | 顯示坐標系,視圖坐標系,用戶坐標系,用戶屏幕 |
C | 用戶控制器,操縱桿 |
邏輯坐標系中的元素: 0,幾何元素Geometry。幾何元素有一個坐標系內唯一的編號(id),記錄了自己的投影(們),記錄了自己受誰影響rely,記錄了自己影響誰influence。 幾何元素分為點Point和點集合PointSet。 1,點就是一個邏輯點,關鍵屬性是邏輯坐標XY。 2,點集是一個接口(interface),因為這款遊戲是尺規作圖,所以點集只分線(Line)和圓(Circle) (為什麽是Circle而不是Ellipse,因為圓規只能畫圓,這裏涉及到圓的定義方式,所以只提供圓工具,橢圓工具可以後續添加) 點集接口有一個交點List<Point2> IPointSet.Intersection(IPointSet another)函數需要後續實現 3,
1 public enum LineType 2 { 3 // 4 // 摘要: 5 // 直線 6 Straight = 0, 7 // 8 // 摘要: 9 // 射線 10 Ray = 1, 11 // 12 // 摘要: 13 // 線段 14 Line = 2 15 }public enum LineType
但是兩點的定義方式並不夠,(p1是第一個點,p2是第二個點),如果用戶是過線(含延長線)和一點做的垂線,這個邏輯也需要體現,
所以我們需要一個LineRely來記錄線的構造(生成)方式
1 public enum LineRely 2 { 3 // 4 // 摘要: 5 // 常規方式,p1,p2兩點確定一條直線 6 Normal = 0, 7 // 8 // 摘要: 9 // 垂線,依賴列表的一條線加上線上或者線外點p1,過p1作依賴點的垂線 10 Perpendicular = 1, 11 // 12 // 摘要: 13 // 垂直平分線,p1,p2兩點連線的中垂線 14 PerpendicularBisector = 2, 15 // 16 // 摘要: 17 // 垂直平分線,p1,p2兩點連線的中垂線 18 AngleBisector = 3, 19 }public enum LineRely
與構造方式LineRely配套的需要一些屬性(字段),比如垂線構造(三個點,一點一線),中垂線構造(兩個點,一個線段),角平分線構造(三個點,兩條射線)等方式
4,圓的構造就容易很多
兩個點,一個是圓心,另一個是圓上的一點
(為什麽不用半徑,因為用戶依靠鼠標的點擊生成圓周上的點,而且這個點是圓的重要的控制點,所以後續半徑需要計算生成)
視圖坐標系中的元素:
一條邏輯線在一個坐標系中有幾個像?是一個嗎?不是。
在這個引擎中,視圖坐標系中的元素是可以直接顯示的,如果你畫的線是一個線段(或者是一條射線),一條邏輯線就會有兩個視圖線,一條實線,一條虛線,先畫虛線(延長線),在畫實線。
視圖坐標系中的元素都是幾何體嗎?不是。(註釋和標簽,待實現)
視圖坐標系中可以放置OutputText
1 /// <summary> 2 /// 屏幕上的提示文本,或者是屏幕上元素的“名字標簽” 3 /// </summary> 4 public class OutputText : ICanOutput 5 { 6 /// <summary> 7 /// 文本內容 8 /// </summary> 9 public string text { get; set; } 10 11 /// <summary> 12 /// 在屏幕上的窗體(canva)中,這個文本“寫”在哪裏 13 /// </summary> 14 public Vector2 viewPoint { get; set; } 15 16 /// <summary> 17 /// 文字顏色 18 /// </summary> 19 public Color fontColor { get; set; } 20 21 /// <summary> 22 /// 文字格式 23 /// </summary> 24 public CanvasTextFormat format { get; set; } 25 26 /// <summary> 27 /// 這個文字是否是某個幾何體的標簽(名字) 28 /// </summary> 29 public Models.Geometry.Geometry rely { get; set; } 30 }OutputText
文本沒有”原像”(借用數學中函數的概念),但是有依賴,比如這個文本是一個點的標簽,依賴保證了文本的坐標始終圍繞在這個點一定範圍周邊,不能被拖動太遠
兩個方向的映射:邏輯到顯示,顯示到邏輯。 M邏輯坐標系是一張無限大的白紙 V視圖坐標系是白紙上的一個矩形框,V中有兩個關鍵的屬性,向量vector,單位長度unitlength, v是這樣一個向量,由邏輯坐標系指向視圖(現實)坐標系的“中心” ul是邏輯坐標系的1單位長度相當於多少 DIP(DIP代表“器件獨立像素”。這是可以與物理像素相同,大於或小於的虛擬化單元。) 映射有兩種方式, “左上角投影”:視圖(現實)坐標系的“中心”在“屏幕”左上角,不關心屏幕的長寬
1 // <summary> 2 /// 邏輯坐標系到屏幕顯示坐標系的轉換(左上角模式) 3 /// </summary> 4 /// <param name="p2"></param> 5 /// <returns>v2</returns> 6 public Vector2 ToVector2Upper_Left_Corner(Point2 p2) 7 { 8 float x = p2.X - vector.X; 9 float y = p2.Y - vector.Y; 10 x = x / unitLength; 11 y = -y / unitLength; 12 Vector2 v2 = new Vector2(x,y); 13 return v2; 14 } 15 /// <summary> 16 /// 屏幕顯示坐標系到邏輯坐標的轉換(左上角模式) 17 /// </summary> 18 /// <param name="v2"></param> 19 /// <returns>Point2 p2</returns> 20 public Point2 ToPoint2Upper_Left_Corner(Vector2 v2) 21 { 22 Point2 p2 = new Point2() { X = (v2.X*unitLength+vector.X), Y = -(v2.Y*unitLength+vector.Y) }; 23 return p2; 24 }左上角是中心
“中心投影”:視圖(現實)坐標系的“中心”在“屏幕”中心,需要知道屏幕的長和寬,然後減半
1 /// <summary> 2 /// 邏輯坐標系到屏幕顯示坐標系的轉換(中心模式) 3 /// </summary> 4 /// <param name="p2"></param> 5 /// <returns>屏幕上的點</returns> 6 public Vector2 ToVector2(Point2 p2) 7 { 8 Vector2 vop = new Vector2() { X = p2.X - vector.X, Y = vector.Y - p2.Y }; 9 //if (WindowHeight <= 0 || WindowWidth <= 0) throw new Exception("中心構造方式需要了解canvas畫布的actual寬和高"); 10 Vector2 half = new Vector2() { X = WindowWidth / 2, Y = WindowHeight / 2 }; 11 Vector2 result = half + vop * unitLength; 12 return result; 13 } 14 /// <summary> 15 /// 屏幕顯示坐標系到邏輯坐標的轉換(中心模式) 16 /// </summary> 17 /// <param name="v2"></param> 18 /// <returns>邏輯坐標系中的點</returns> 19 public Point2 ToPoint2(Vector2 v2) 20 { 21 if (WindowHeight <= 0 || WindowWidth <= 0) throw new Exception("中心構造方式需要了解canvas畫布的actual寬和高"); 22 Vector2 half = new Vector2() { X = WindowWidth / 2, Y = WindowHeight / 2 }; 23 Vector2 vop = (v2 - half) / unitLength; 24 Point2 p2 = new Point2() { X = (vop.X + vector.X), Y = -vop.Y + vector.Y }; 25 return p2; 26 }屏幕中心是中心
四種操作,“增刪改查”:用戶可能對一個幾何體有定義(創建),移動,變形,刪除等操作,這些操作都會被記錄在操作棧中,這樣用戶可以撤銷重做。查詢操作主要包含在代碼邏輯中,也是必不可少的。
“繁瑣”的“綁定”:對於一個引擎的每一個“零件”,都應該記錄豐富的信息,這種豐富可能是繁瑣多余的,但是會給操作帶來方便。
邏輯坐標系知道有多少用戶在看自己(邏輯坐標系綁定顯示坐標系(們)),顯示坐標系知道自己看的是誰(顯示坐標系綁定邏輯坐標系),邏輯坐標系知道自己裏面有多少邏輯幾何元素,每個邏輯幾何元素知道自己在哪個邏輯坐標系中,視圖坐標系知道自己裏面有多少視圖幾何元素或其他,每個視圖幾何元素知道自己位於哪個視圖坐標系中,每個邏輯幾何元素知道自己有多少個視圖幾何元素,每個視圖幾何元素知道自己是哪個邏輯幾何元素的“投影像”。
Geomystery(幾何迷城)的遊戲引擎設計與實現