1. 程式人生 > >Geomystery(幾何迷城)的遊戲引擎設計與實現

Geomystery(幾何迷城)的遊戲引擎設計與實現

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 邏輯坐標系,系統坐標系
V 顯示坐標系,視圖坐標系,用戶坐標系,用戶屏幕
C 用戶控制器,操縱桿
“橫看成嶺側成峰,遠近高低各不同。不識廬山真面目,只緣身在此山中。”廬山只有一座,而在同一時刻,不同的人看廬山,能看到不同的廬山。所以對於一個“邏輯的”坐標系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來記錄線的線型 技術分享
 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(幾何迷城)的遊戲引擎設計與實現