2015年9月1日星期二

[Unity3D] 初嘗插件Grids Pro 與 Hex/Isometric tiles 的合作


前言

故事開始之先想先讓各位認識一下 2D Isometric Game,我想起兩個比較有名的遊戲:世紀帝國II和暗黑破壞神II


其實它們是用了Isometric Tiled Map 就是用2D去模仿3D的感覺,例如你看下圖的地圖,每一個正方體(Cube)看似立體:

每一個正方體都是平面(2D)的!只是畫出來好像立體(3D)~


你看到的地圖其實只是一張平面的畫~以前是怎樣將一用2D的素材去模仿3D的環境?就像我們怎樣畫畫是怎樣畫出立體的感覺?

這就要說起透視投影(Perspective projection)和平行投影(Parallel projection),而Isometric Game用的是等軸測投影(Isometric Projection),這是源自Parallel projection

簡單點說就是假設光線是平行,物件不會因為遠了而縮小但也產生了3D的感覺

如果引發了大家對Isometric Projection/Game 興趣的話,可以參考一下以下網址
Isometric game 及譯法漫談:http://www.ituring.com.cn/article/788
或是問問偉大的Google大神!


插件介紹

好了故事開始了,事源是小弟要弄一個回合制的2D遊戲,有一天在Asset Store找到一個畫得好漂亮的Isometric Tile,而且還有小弟想用的六角形版本(Hexagonal)
Golden Skull Studio出品的Tiles:
Hexagonal: https://www.assetstore.unity3d.com/en/#!/content/37170
Isometric Tile: https://www.assetstore.unity3d.com/en/#!/content/27944

然後想起之前買了GameLogic的Grids Pro
Grids Pro:
https://www.assetstore.unity3d.com/en/#!/content/9968

希望可以用Isometric Tile 和Grids Pro 是開發這款遊戲~
可是,因為每一個Isometric Tile其實只是一個平面,用它們砌地圖的話會有重疊,配合Grids Pro的坐標系統比較麻煩(就是要自己計數呀~),或是地圖跟坐標是分開的,只是再把它們拼湊一齊......

就在我苦惱的時候,有天Gamelogic的大大們終於把它們合體了!
http://gamelogic.co.za/grids/golden-skull-studio-extension-for-grids/
好感動呀!!!它們真的計出來了!!!

然後就跟住以上網址的指示,把Grids Pro和Isometric Tile/Hexagonal 兩插件, GoldenSkullGridExtensions.unitypackage 戴入到專案就可以了~

順帶一提記得把所有Tiles的Pixels Per Unit設定為1 !
Package裏還提供Random, Patten 跟Height 三款地圖,不過暫時不先對它們再作研究


實現物件在地圖上移動

然後就想先試試讓物件在地圖上移動

首先,要給這個物件(Player)記住自己的位置:
public class Player : MonoBehaviour {
 private FlatHexPoint _currentPointLocation;
  
 public FlatHexPoint CurrentPointLocation
 {
   get;
   set;
 }
}

然後在遊戲開之先把Player的坐標位置設定為(0,0)
void Start () {
  player.CurrentPointLocation = FlatHexPoint.Zero;
  player.transform.position = new Vector3(Map[player.CurrentPointLocation].x, Map[player.CurrentPointLocation].y);
  start = player.CurrentPointLocation;
 }

然後按下其中一個格(Cell)就可以引發OnClick, point 就是被按的那一格,不過記得在Grids Builder裏勾上 is interactive ,否則不用發動OnClick喔!


public void OnClick(FlatHexPoint point)
 {
Debug.Log (point.BasePoint);   //return (x,y)
  
  if(player.CurrentPointLocation != null)
  {
   start = player.CurrentPointLocation;
   finish = point;
   StartCoroutine(Move(start, finish));
  }
 }
然後就是要物件在實際的地圖的移動
public IEnumerator Move(FlatHexPoint currentPoint, FlatHexPoint endPoint){
  float time = 0;
  const float totalTime = .3f;
 
  while (time < totalTime)
  {
   float x = Mathf.Lerp(Map[currentPoint].x, Map[endPoint].x, time / totalTime);
   float y = Mathf.Lerp(Map[currentPoint].y, Map[endPoint].y, time / totalTime);

   y -= 20;

   player.transform.position = new Vector3(x, y);
   time += Time.deltaTime;
  }
  
  player.CurrentPointLocation  = endPoint;
  yield return null;
 }

然後就變成這樣了:

不知大家有沒有發現有甚麽問題?
無錯!比卡超其實不是逐格移動然後去到目標格,牠只是飄去目標格~

所以我們要用到Path-finding這東西,就是要格與格之間的最快路線:
http://gamelogic.co.za/grids/documentation-contents/quick-start-tutorial/path-finding-grids-for-unity/


Grids Pro 好用的地方就是讓很多代碼變得簡單易明,我們這要把開始點(start)和終點(finish)放進AStar裏就會出現路線了:
var path = Algorithms.AStar(Grid, start, finish);

拿了路線之後我們先建立MovePath這個function, 它會再call 我們剛才弄好的Move(FlatHexPoint currentPoint, FlatHexPoint endPoint) 讓比卡超逐格移動!

if(player.CurrentPointLocation != null)
  {
   start = player.CurrentPointLocation;
   finish = point;
   var path = Algorithms.AStar(Grid, start, finish);
   StartCoroutine(MovePath(path));
  }

public IEnumerator MovePath(IEnumerable path)
 {
  var pathList = path.ToList();
  
  if(pathList.Count < 2) yield break; //Not a valid path
  
  for(int i = 0; i < pathList.Count - 1; i++)
  {
   yield return StartCoroutine(Move (pathList[i], pathList[i+1]));
  }
  }




不知大家有沒有留意地圖在下雨!其實這是之前減價看上了的插件:
https://www.assetstore.unity3d.com/en/#!/content/33229

漂亮的東西總讓人忍不住買下來~

5 則留言:

  1. 回覆
    1. Map是Grids Pro library裏的,會回傳物件實際的位置
      例如player.CurrentPointLocation是player的坐標,假設是(0,0)吧, 但我們不知道坐標(0,0)在環境上的位置(就是指transform裏的position)所以用Map
      Map[player.CurrentPointLocation]就知道(0.0)的x,y,z是(-573,240,0)了

      刪除
  2. 少年,真土豪!你这一篇下来买了不少插件

    回覆刪除
    回覆
    1. 那時常常逛Asset Store留意特價~特價來了就忍王住買下來 XD

      刪除