[Unity遊戲作品]Unity初體驗--數獨小遊戲與它的程式寫法


  各位朋友大家好,這次要來分享在新公司的一些遊戲作品,數獨遊戲。是某朋友敲碗要小弟打這篇小弟才打的,本來有點懶得打XD



  上一次小弟做遊戲的時候已經是大四(2008年)的時候了(凱特衝浪去遊戲專案請按此),壓根沒有想到過了13年,現在還有機會碰遊戲相關的專案。因為現在換到遊戲公司工作,才得以碰到做遊戲這塊,而且其實遊戲產業也是小弟夢寐以求想做的工作,所以做得算是開心~只是薪水....(´_ゝ`)




數獨小遊戲下載點:
https://drive.google.com/file/d/1zVGfm60gll8qRUM4RDKeMhoW8QwQHLbo

數獨小遊戲Unity原始檔:
https://github.com/hcyang1227/Proj/tree/main/001_Sudoku



  這次先來分享遊戲畫面跟遊戲流程。畫面影片如上,流程很簡單,先用亂數產生一個盤面,再從盤面挖洞將滿滿的格子當中挖掉一部分的數字,剩下的空格就是玩家要填空的部分了。小弟也做了自動填空跟目測檢查有無兩種以上解答的功能。


  因為這個專案當初公司只給了4天就要寫出來,小弟對Unity跟C#(Unity預設使用的程式語言)又不是很熟的狀態,所以寫得有點吃力,就算參考網路上的寫法,也寫不太出來,可說是差點就趕不出來的狀態,看著同事輕鬆解決這個專案,只能遠目(不過他幫了我很多忙,感恩同事,讚歎同事)。


  在主程式演算法方面,下面有參考的C#程式碼供大家參考,應該是沒什麼bug才對,頂多UI的部分寫得不是很好而已。這邊描述一下如何產生一個亂數產生的數獨盤面。


  1. 首先先寫GetRandomArray()使程式產生一個1~9的有序陣列,接著用亂數將這個有序列逐一交換數字順序好幾次

  2. 分別產生1~9的有序陣列並填入左上、中間、右下的九宮格內



  3. 執行SolveSudoku(),先將盤面上的數字存進sudoku陣列,接著找出盤面上數值為零(未填數字)的地方並存入陣列n[]

  4. 接著執行forcefillsdk(1),用遞迴法搜尋可能解答(若此格isValid判定答案正確則執行下一格forcefillsdk(i+1),如果碰壁則自動回上一個動作),只要在solution1[]還是空白的狀況下就用正向搜尋(如果isValid判定答案不對則加1),solution1[]有數值時就用逆向搜尋(如果isValid判定答案不對則減1)



  5. 等到solution1[]與solution2[]都有答案時(或是搜索次數超過30000次時),程式就會自動終止並秀出最終結果







  程式碼截圖如上、擷取如下,寫得也不是很好,僅供參考就好XD



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

public class Sudoku : MonoBehaviour
{
public GameObject uiText;
public GameObject InputFld;
public GameObject BtnDigHole;
Color BtnColor = new Color(149, 169, 245, 255);
int solveStep = 0;
int[,] sudoku = new int[9, 9];
int[,] solution1 = new int[9, 9];
int[,] solution2 = new int[9, 9];
int[,] n = new int[81, 2];
int nlen = 0;

static int[,] sudokuSample = new int[9, 9]
        {
        {0,0,0,7,2,8,0,0,0},
        {0,9,0,0,5,1,6,0,0},
        {0,0,0,0,6,0,0,8,2},
        {3,0,0,8,0,2,7,0,4},
        {1,7,4,0,3,0,0,2,0},
        {2,8,0,5,0,0,0,3,0},
        {0,1,0,3,0,0,2,0,0},
        {0,0,7,0,4,6,0,0,5},
        {0,0,6,1,0,0,0,4,9}};

	public void SudokuSolution1()
	{
		for(int i=0;i().color = BtnColor;
			}
		}
		uiText.GetComponent().text = "答案1盤面";
		Show();
	}
	public void SudokuSolution2()
	{
		for(int i=0;i().color = BtnColor;
			}
		}
		uiText.GetComponent().text = "答案2盤面";
		Show();
	}
	public void SudokuSample()
	{
		BtnDigHole.SetActive(false);
		for(int i=0;i().color = Color.gray;
				}
				else
				{
				string str="Btn"+(i+1)+"-"+(j+1);
				GameObject Btn = GameObject.Find(str);
				Btn.GetComponent().color = BtnColor;
				}
			}
		}

		uiText.GetComponent().text = "使用數獨樣本";
		Show();

	}

	public void InitSudoku()
	{
		BtnDigHole.SetActive(false);
		for (int i=0;i().color = Color.gray;
			}
		}
		Show();
		solveStep = 0;
		uiText.GetComponent().text = "清空盤面";


	}

    int[] GetRandomArray()
	{
		int[] result = new int[] {1,2,3,4,5,6,7,8,9};
		int loopNum = UnityEngine.Random.Range(10,100);
		for (int i = 0; i < loopNum; i++)
		{
			int a = UnityEngine.Random.Range(0,9);
			int b = UnityEngine.Random.Range(0,9);
            while(a == b)
            {
                b = UnityEngine.Random.Range(0,9);
            }
			int temp = 0;
			temp = result[a];
			result[a] = result[b];
			result[b] = temp;
		}
		return result;
	}

	public void DiggingHole()
	{
		BtnDigHole.SetActive(false);

		int digHoleNum = 15;
		string digHoleText = InputFld.GetComponent().text;

		if ((digHoleText == "") || (int.Parse(digHoleText) > 50) || (int.Parse(digHoleText) <= 0))
			{
				InputFld.GetComponent().text = "15";
				digHoleNum = 15;
				uiText.GetComponent().text = "進行挖洞,共挖15個洞";
			}
		else
			digHoleNum = int.Parse(digHoleText);
			uiText.GetComponent().text = "進行挖洞,共挖"+digHoleNum.ToString()+"個洞";

		for (int i = 0; i < digHoleNum; i++)
		{
			int a = UnityEngine.Random.Range(0,9);
			int b = UnityEngine.Random.Range(0,9);
            while(sudoku[a,b] == 0)
            {
			a = UnityEngine.Random.Range(0,9);
			b = UnityEngine.Random.Range(0,9);
            }
			sudoku[a,b] = 0;
			string str="Btn"+(a+1)+"-"+(b+1);
			GameObject Btn = GameObject.Find(str);
			Btn.GetComponent().color = Color.gray;
		}
		Show();
	}

    void SolveSudoku()
	{
		nlen = 0;
		n = new int[81, 2];
		solution1 = new int[9, 9];
		solution2 = new int[9, 9];

		//將盤面上的文字存入sudoku
		for (int i = 1; i <= 9; i++)
		{
			for (int j = 1; j <= 9; j++)
			{
                string str="Btn"+i+"-"+j+"/Text";
				GameObject Btn = GameObject.Find(str);
                sudoku[i-1,j-1] = int.Parse(Btn.GetComponent().text);
				Btn.GetComponentInParent().color = BtnColor;
			}
		}
		//搜尋數獨區塊為零的地方
		for (int i = 0; i < 9; i++)
		{
			for (int j = 0; j < 9; j++)
			{
				if(sudoku[i,j]==0)
				{
					n[nlen, 0] = i;
					n[nlen, 1] = j;
					nlen++;
				}
			}
		}
		//用遞迴法搜尋可能解答
		forcefillsdk(1);


	}

	void forcefillsdk(int i)
	{
		if(solveStep>=30000)
		{
			uiText.GetComponent().text = "解題超過30000步,強制終止";
			return;
		}
		if((i > nlen) && (solution1[0,0] == 0))
		{
			Show();
			for(int a=0;a().text = "完成解題,找到答案1";
			//所有空格填回0
			for (int k = 0; k < nlen; k++)
			{
				sudoku[n[k, 0],n[k, 1]] = 0;
			}
			forcefillsdk(1);
			return;
		}
		if((i > nlen) && (solution1[0,0] != 0) && (solution2[0,0] == 0))
		{
			Show();
			for(int a=0;a().text = "完成解題,找到答案1與答案2";
			return;
		}
		if(i > nlen)
		{
			uiText.GetComponent().text = "完成解題,終止程序";
			return;
		}
		for (int k = 1; k <= 9; k++)
		{
			if (solution1[0,0] == 0)
				sudoku[n[i-1, 0],n[i-1, 1]] += 1;	//解答一用正向搜尋
			else
				sudoku[n[i-1, 0],n[i-1, 1]] -= 1;	//解答二用逆向搜尋

			if (sudoku[n[i-1, 0],n[i-1, 1]] > 9)
			{
				sudoku[n[i-1, 0],n[i-1, 1]] = 1;
			}

			if (sudoku[n[i-1, 0],n[i-1, 1]] < 0)
			{
				sudoku[n[i-1, 0],n[i-1, 1]] = 9;
			}

			if(isValid(n[i-1, 0], n[i-1, 1]))
			{
				// print("第"+i+"("+n[i-1, 0]+","+n[i-1, 1]+")被填滿"+sudoku[n[i-1, 0],n[i-1, 1]]);
				solveStep++;
				forcefillsdk(i+1);
			}
			else
			{
				solveStep++;
			}
		}
	// print("第"+i+"("+n[i-1, 0]+","+n[i-1, 1]+")錯誤,數字歸零");
	sudoku[n[i-1, 0],n[i-1, 1]] = 0;
	}

    //驗證函數
	bool isValid(int i, int j)
	{
		int n = sudoku[i,j];
		int[] query = new int[9] {0,0,0,3,3,3,6,6,6};
		int t, u;
		//本身是否為零
		if (sudoku[i,j] == 0)
			return false;
        //每一行每一列是否重複
		for (t=0;t().text);
			}
		}
		bool Correctness = true;
		for (int i = 0; i < 9; i++)
		{
			for (int j = 0; j < 9; j++)
			{
                if(isValid(i,j) == false)
				{
					Correctness = false;
					string str="Btn"+(i+1)+"-"+(j+1);
					GameObject Btn = GameObject.Find(str);
					Btn.GetComponent().color = Color.red;
				}
				else
				{
					string str="Btn"+(i+1)+"-"+(j+1);
					GameObject Btn = GameObject.Find(str);
					Btn.GetComponent().color = BtnColor;
				}
			}
		}
		if (Correctness)
		{
			uiText.GetComponent().text = "數獨盤面正確無誤";
			BtnDigHole.SetActive(true);
		}
		else
			uiText.GetComponent().text = "數獨盤面有以下紅色錯誤";

	}
	void Show()
	{
		for (int i = 1; i <= 9; i++)
		{
			for (int j = 1; j <= 9; j++)
			{
                string str="Btn"+i+"-"+j+"/Text";
				GameObject Btn = GameObject.Find(str);
                Btn.GetComponent().text = ""+sudoku[i-1,j-1];
			}
		}
	}

    public void CreateGameFull()
    {
		uiText.GetComponent().text = "開始產題...";
			solveStep = 0;
			InitSudoku();
			for (int i = 0; i < 3; i++)
			{
				int[] temp = GetRandomArray();
				int p = 0;
				for (int j =0+(i*3);j().text);
			}
		}
		solveStep = 0;
		SolveSudoku();
		solveStep = 0;
	}

    void Start()
    {
		BtnDigHole.SetActive(false);
    }

    void Update()
    {

    }
}
<9 btn.getcomponent="" btn="GameObject.Find(str);" for="" gameobject="" i="" int="" j="" mage="" solution1="" str="Btn" string="" sudoku=""><9 btn.getcomponent="" btn="GameObject.Find(str);" for="" gameobject="" i="" int="" j="" mage="" solution2="" str="Btn" string="" sudoku=""><9 0="" btn.getcomponent="" btn="GameObject.Find(str);" for="" gameobject="" i="" if="" int="" j="" mage="" str="Btn" string="" sudoku="" sudokusample=""><9 0="" btn.getcomponent="" btn="GameObject.Find(str);" for="" gameobject="" i="" int="" j="" mage="" str="Btn" string="" sudoku=""><9 a="" b="" ext="" for="" int="" solution1="" sudoku="" uitext.getcomponent=""><9 a="" b="" ext="" for="" int="" solution2="" sudoku="" uitext.getcomponent=""><9 3="" 9="" btn="GameObject.Find(str);" checksdkcorrectness="" ext="" false="" for="" gameobject="" i-1="" i="" if="" int.parse="" int="" j-1="" j="" n="" public="" query="" return="" str="Btn" string="" sudoku="" t="" tn.getcomponent="" true="" u="" void="">


  這次的數獨撰寫,讓我對小時候寫遊戲程式的熱誠慢慢又找回來了,希望未來會有更多的作品可以跟大家分享,也期待大家能夠多多支持國產遊戲作品,因為設計一款好遊戲真的不容易~ヽ(∀゚ )人(゚∀゚)人( ゚∀)人(∀゚ )人(゚∀゚)人( ゚∀)ノ

沒有留言:

張貼留言