1. Trang chủ
  2. » Giáo Dục - Đào Tạo

báo cáo lập trình game trò chơi 2048

16 93 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 16
Dung lượng 240,18 KB

Nội dung

HỌC VIỆN CƠNG NGHỆ BƯU CHÍNH VIỄN THƠNG - - BÁO CÁO Trò chơi: 2048 Giảng viên hướng dẫn Sinh viên thực Mơn học Lớp Nhóm : Đỗ Thị Liên : Nguyễn Đức Quang - B18DCPT179 Nguyễn Tùng Lâm – B18DCPT124 Nguyễn Chí Hiếu – B18DCPT084 : Lập trình game : D18PTDPT2 : Nhóm 16 – Kíp – Thứ Hà Nội, tháng 12 năm 2021 Giới thiệu game: 2048 trò chơi tiếng đơn giản khiên người chơi tham gia đến hàng Mục tiêu 'hợp nhất' có giá trị giống hệt với nhau, để giá trị chúng nhân đôi Khi người chơi vuốt theo hướng mong muốn, vật phẩm di chuyển đến vật phẩm tạo Nếu người chơi đạt đến số 2048 họ thắng trị chơi Game Objects: - Main Camera: Là camera để hiển thị trị chơi Script Holder: Chứa scripts trò chơi Canvas: Chứa UI trò chơi ScoreText: Dòng ghi số điểm đạt người chơi RestartButton: Nút bắt đầu lại trò chơi EventSystem: Xử lý kiện trị chơi Background: Chứa background trị chơi Prefabs: Có Game Object 2, 4, 8,…, 1024, blank Scripts: - Start: public void PlayGame() { SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex + 1); } Khi nhấn vào nút Play, trò chơi tự động load từ scene Start sang scene GameScene - Input Methods Nhóm triển khai hai phương pháp để lấy thông tin đầu vào người dùng trò chơi: Qua phím mũi tên bàn phím, vuốt hình cảm ứng (hoặc chuột) Nhóm triển khai phép liệt kê để lấy thông tin đầu vào người dùng giao diện triển khai theo phương thức nhập liệu sử dụng public enum InputDirection { Left, Right, Top, Bottom } public interface IInputDetector { InputDirection? DetectInputDirection(); } • Input Methods qua bàn phím: public class ArrowKeysDetector : MonoBehaviour, IInputDetector { public InputDirection? DetectInputDirection() { if (Input.GetKeyUp(KeyCode.UpArrow)) return InputDirection.Top; else if (Input.GetKeyUp(KeyCode.DownArrow)) return InputDirection.Bottom; else if (Input.GetKeyUp(KeyCode.RightArrow)) return InputDirection.Right; else if (Input.GetKeyUp(KeyCode.LeftArrow)) return InputDirection.Left; else return null; } } • Input Methods qua cảm ứng: public enum State { SwipeNotStarted, SwipeStarted } Tạo enum đơn giản để giữ trạng thái việc vuốt: public class SwipeDetector : MonoBehaviour, IInputDetector { private private private private private State state = State.SwipeNotStarted; Vector2 startPoint; DateTime timeSwipeStarted; TimeSpan maxSwipeDuration = TimeSpan.FromSeconds(1); TimeSpan minSwipeDuration = TimeSpan.FromMilliseconds(100); Sử dụng số trường để giúp ta kế thừa Thời lượng vuốt tối đa giây tối thiểu 100 mili giây public InputDirection? DetectInputDirection() { if (state == State.SwipeNotStarted) { if (Input.GetMouseButtonDown(0)) { timeSwipeStarted = DateTime.Now; state = State.SwipeStarted; startPoint = Input.mousePosition; } } Nếu người dùng chạm (hoặc nhấp chuột) vào hình, bắt đầu trình vuốt else if (state == State.SwipeStarted) { if (Input.GetMouseButtonUp(0)) { TimeSpan timeDifference = DateTime.Now - timeSwipeStarted; if (timeDifference = minSwipeDuration) { Vector2 mousePosition = Input.mousePosition; Vector2 differenceVector = mousePosition - startPoint; float angle = Vector2.Angle(differenceVector, Vector2.right); Vector3 cross = Vector3.Cross(differenceVector, Vector2.right); Khi người dùng kết thúc trình vuốt, ta kiểm tra xem thời lượng vuốt có nằm giới hạn thời gian mong muốn hay không Nếu vậy, phải tìm góc vuốt qua phương thức Vector2.Angle Ta sử dụng phương pháp Vector3.Cross (tính tích số chéo) để xác định hướng chênh lệch if (cross.z > 0) angle = 360 - angle; state = State.SwipeNotStarted; if ((angle >= 315 && angle < 360) || (angle >= && angle 45 && angle 135 && angle = Globals.Rows || toCheckColumn >= Globals.Columns) return null; if (matrix[originalRow, originalColumn] != null && matrix[toCheckRow, toCheckColumn] != null && matrix[originalRow, originalColumn].Value == matrix[toCheckRow, toCheckColumn].Value && !matrix[toCheckRow, toCheckColumn].WasJustDuplicated) { //double the value, since the item is duplicated matrix[toCheckRow, toCheckColumn].Value *= 2; matrix[toCheckRow, toCheckColumn].WasJustDuplicated = true; //make a copy of the gameobject to be moved and then disappear var GOToAnimateScaleCopy = matrix[originalRow, originalColumn].GO; //remove this item from the array matrix[originalRow, originalColumn] = null; return new ItemMovementDetails(toCheckRow, toCheckColumn, matrix[toCheckRow, toCheckColumn].GO, GOToAnimateScaleCopy); } else { return null; } } Phương thức kiểm tra xem hai ô truyền dạng đối số (thông qua lập cột / hàng chúng) có giá trị hay khơng Đầu tiên, kiểm tra xem thơng qua có nằm ngồi giới hạn hay khơng Sau đó, kiểm tra xem vị trí mảng có phải giá trị rỗng hay khơng khơng bị trùng lặp (tức khơng bị trùng lặp sau lần vuốt tại) Nếu tất kiểm tra đúng, - chép giá trị đặt trường WasJustDuplicated thành true - xóa ô thứ hai khỏi mảng sau ta giữ tham chiếu đến nó, để tạo hiệu ứng cho - trả thể lớp ItemMovementDetails, mang thơng tin để có vị trí để có hoạt ảnh theo tỷ lệ (và cuối cùng, biến mất) private ItemMovementDetails MoveItemToNullPositionAndCheckIfSameWithNextOne (int oldRow, int newRow, int itemToCheckRow, int oldColumn, int newColumn, int itemToCheckColumn) { //we found a null item, so we attempt the switch ;) //bring the first not null item to the position of the first null one matrix[newRow, newColumn] = matrix[oldRow, oldColumn]; matrix[oldRow, oldColumn] = null; //check if we have the same value as the next one ItemMovementDetails imd2 = AreTheseTwoItemsSame(newRow, newColumn, itemToCheckRow, itemToCheckColumn); if (imd2 != null)//we have, so add the item returned by the method { return imd2; } else//they are not the same, so we'll just animate the current item to its new position { return new ItemMovementDetails(newRow, newColumn, matrix[newRow, newColumn].GO, null); } } Phương pháp di chuyển mặt hàng đến nơi mà phải đến (dựa việc kiểm tra giá trị) Nó định vào vị trí "vơ hiệu hóa" ô cũ Hơn nữa, kiểm tra xem vật phẩm bên cạnh có giá trị hay khơng Trị chơi có hai phương pháp để di chuyển Một gọi vuốt theo chiều ngang cho vuốt dọc public List MoveHorizontal(HorizontalMovement horizontalMovement) { ResetWasJustDuplicatedValues(); var movementDetails = new List(); //the relative column we will compare with //if swipe is left, we will compare with the previous one (the -1 position) int relativeColumn = horizontalMovement == HorizontalMovement.Left ? -1 : 1; //to get the column indexes, to the loop below var columnNumbers = Enumerable.Range(0, Globals.Columns); //for left swipe, we will traverse the columns in the order 0,1,2,3 //for right swipe, we want the reverse order if (horizontalMovement == HorizontalMovement.Right) { columnNumbers = columnNumbers.Reverse(); } Phương thức bắt đầu cách đặt lại tất giá trị WasJustDuplicated Sau đó, tùy thuộc vào chuyển động sang trái hay phải, nhận –1 Điều giúp xác định ô cần so sánh Nếu vuốt sang trái, di chuyển tất sang trái, cần so sánh ô với ô trước (–1 một), để kiểm tra độ giống Ta sử dụng phương thức Enumerable.Range để lấy index cột Phương thức trả danh sách chứa [0,1,2,3,…, Globals.Columns-1] Nếu vuốt sang phải, đảo ngược thứ tự danh sách columnNumbers Điều cần lặp lại cột theo hướng xác Nếu vuốt sang trái, trị chơi bắt đầu cách kiểm tra cột để tìm giá trị rỗng, sau cột thứ hai, v.v Đây lý trò chơi muốn di chuyển khơng phải null sang vị trí rỗng đầu tiên, bên trái Nếu vuốt sang phải, ta cần thực thao tác theo hướng ngược lại Nên ta phải đảo ngược danh sách columnNumbers for (int row = Globals.Rows - 1; row >= 0; row ) { //we're doing foreach instead of for in order to traverse the columns //in the appropriate order foreach (int column in columnNumbers) { //if the item is null, continue checking for non-null items if (matrix[row, column] == null) continue; //since we arrived here, we have a non-null item //first we check if this item has the same value as the previous one //previous one's position depends on whether the relativeColumn variable is -1 or 1, depending on the swipe ItemMovementDetails imd = AreTheseTwoItemsSame(row, column, row, column + relativeColumn); if (imd != null) { //items have the same value, so they will be "merged" movementDetails.Add(imd); //continue the loop //the new duplicated item may be moved on a subsequent loop continue; } Ở bắt đầu vòng lặp trò chơi Trò chơi kiểm tra tất hàng Sau đó, ta lặp qua tất cột, lấy ô từ danh sách columnNumbers Trong duyệt qua hàng, trước tiên, trò chơi kiểm tra xem có rỗng khơng Nếu rỗng, ta tiếp tục kiểm tra ô (bằng cách kiểm tra cột - có nghĩa –1 1, tùy thuộc vào lần vuốt Khi đến cột không rỗng, ta kiểm tra xem cột có giống với cột bên cạnh Một lần nữa, “bên cạnh” có nghĩa –1 1, tùy thuộc vào việc vuốt sang trái hay phải Nếu giống nhau, ta thêm thơng tin vào danh sách MovementDetails tiếp tục vòng lặp cột //matrix[row,column] is the first not null item //move it to the first null item space int columnFirstNullItem = -1; //again, this is to help on the foreach loop that follows //for a left swipe, we want to check the columns to [column-1] //for a right swipe, we want to check columns [Globals.Columns-1] to column+1 int numberOfItemsToTake = horizontalMovement == HorizontalMovement.Left ? column : Globals.Columns – column; bool emptyItemFound = false; //keeping it for documentation/clarity //this for loop would run for a left swipe ;) //for (columnFirstNullItem = 0; columnFirstNullItem < column; columnFirstNullItem++) foreach (var tempColumnFirstNullItem in columnNumbers.Take(numberOfItemsToTake)) { //keep a copy of the index on the potential null item position columnFirstNullItem = tempColumnFirstNullItem; if (matrix[row, columnFirstNullItem] == null) { emptyItemFound = true; break;//exit the loop } } Nếu khơng giống nhau, phải di chuyển ô mà tham chiếu đến vị trí rỗng Đối với thao tác vuốt sang trái, ô [hàng, cột], vị trí từ [hàng, 0] đến [hàng, cột-1], cần ô cột từ danh sách Số cột Đối với thao tác vuốt sang phải, vị trí từ [hàng, Globals.Columns-1] đến [hàng, cột + 1], cần Globals.Columns - ô cột từ danh sách Cột Số bị đảo ngược Ta thực vòng lặp cột (sử dụng phương pháp Take LINQ) giữ tham chiếu đến số cột (thông qua biến columnFirstNullItem) kiểm tra rỗng Nếu tìm thấy một, ta khỏi vịng lặp //we did not find an empty/null item, so we cannot move current item if (!emptyItemFound) { continue; } ItemMovementDetails newImd = MoveItemToNullPositionAndCheckIfSameWithNextOne (row, row, row, column, columnFirstNullItem, columnFirstNullItem + relativeColumn); movementDetails.Add(newImd); } } return movementDetails; } Nếu ta khơng tìm thấy rỗng, tham chiếu vị trí nó, ta để nguyên Nếu làm vậy, di chuyển ô tham chiếu đến vị trí rỗng tạo thể lớp ItemMovementDetails, để mang thông tin hoạt ảnh Ở cuối phương thức MoveHoriz ngang, ta trả danh sách MovementDetails, chứa thông tin cho tất hoạt ảnh phải thực - GameManager: public enum GameState { Playing, Won } public class GameManager : MonoBehaviour { private GameState gameState = GameState.Playing; ItemArray matrix; public GameObject GO2, GO4, GO8, GO16, GO32, GO64, GO128, GO256, GO512, GO1024, blankGO; public Text ScoreText, DebugText; private float distance = 0.109f; public IInputDetector inputDetector; private int ZIndex = 0, score = 0; Lớp GameManager có hai trạng thái, Chơi Thắng Ta khai báo tất có liên quan GameObjects (2,4,8,…), hai ô Văn (một ô để hiển thị điểm ô khác để gỡ lỗi) Phần trốngGO sử dụng làm hình cho vị trí trống (null) //will read a file from Resources folder //and create the matrix with the preloaded data void InitArrayWithPremadeData() { string[,] sampleArray = Utilities.GetMatrixFromResourcesData(); for (int row = 0; row < Globals.Rows; row++) { for (int column = 0; column < Globals.Columns; column++) { int value; if (int.TryParse(sampleArray[Globals.Rows - - row, column], out value)) { CreateNewItem(value, row, column); } } } } Phương thức sử dụng để tạo mảng thông qua thông tin thu GetMatrixFromResourcesData mơ tả trước public void Initialize() { if (matrix != null) for (int row = 0; row < Globals.Rows; row++) for (int column = 0; column < Globals.Columns; column++) { } if (matrix[row, column] != null && matrix[row, column].GO != null) Destroy(matrix[row, column].GO); matrix = new ItemArray(); //InitArrayWithPremadeData(); CreateNewItem(); CreateNewItem(); score = 0; UpdateScore(0); } gameState = GameState.Playing; Phương thức Khởi tạo gọi trị chơi bắt đầu sau người chơi chạm vào nút Khởi động lại Đầu tiên, phá hủy tất vật phẩm có trị chơi Sau đó, khởi tạo mảng, tạo hai khởi tạo biến điểm biến gameState private void CreateNewItem(int value = 2, int? row = null, int? column = null) { int randomRow, randomColumn; if (row == null && column == null) { matrix.GetRandomRowColumn(out randomRow, out randomColumn); } else { randomRow = row.Value; randomColumn = column.Value; } var newItem = new Item(); newItem.Row = randomRow; newItem.Column = randomColumn; newItem.Value = value; GameObject newGo = GetGOBasedOnValue(value); newGo.transform.localScale = new Vector3(0.01f, 0.01f, 0.01f); newItem.GO = Instantiate(newGo, this.transform.position + new Vector3(randomColumn + randomColumn * distance, randomRow + randomRow * distance, ZIndex), Quaternion.identity) as GameObject; newItem.GO.transform.scaleTo(Globals.AnimationDuration, new Vector3(1.0f, 1.0f, 1.0f)); matrix[randomRow, randomColumn] = newItem; } Phương thức CreateNewItem sử dụng để tạo mảng Nó sử dụng đầu trò chơi sau lần vuốt người dùng Như bạn thấy, có đối số tùy chọn Những điều cho phép người gọi gọi phương thức theo cách khác nhau, tùy thuộc vào chức mong muốn hàm Nếu đối số hàng cột rỗng, điều có nghĩa muốn đặt ô vị trí ngẫu nhiên Nếu chúng khơng rỗng, điều có nghĩa ta gỡ lỗi mức thông qua tệp tạo sẵn thư ô Tài nguyên Tương tự viết tắt đối số giá trị, ngoại trừ thực tế điều có giá trị từ cấp tạo trước có giá trị (giá trị mặc định cho ô mới) Phương thức tạo phiên lớp Item, gán trường thích hợp cho nó, khởi tạo GameObject dựa giá trị item tạo hiệu ứng cho quy mơ thơng qua thư viện hoạt hình GoKit / tween tuyệt vời Cuối cùng, định cá thể vào vị trí thích hợp mảng private void InitialPositionBackgroundSprites() { for (int row = 0; row < Globals.Rows; row++) { for (int column = 0; column < Globals.Columns; column++) { Instantiate(blankGO, this.transform.position + new Vector3(column + column * distance, row + row * distance, ZIndex), Quaternion.identity); } } } Phương thức InitialPositionBackgroundSosystemes tạo cho tất vị trí mảng void Update() { if (gameState == GameState.Playing) { InputDirection? value = inputDetector.DetectInputDirection(); if (value.HasValue) { List movementDetails = new List(); //Debug.Log(value); if (value == InputDirection.Left) movementDetails = matrix.MoveHorizontal(HorizontalMovement.Left); else if (value == InputDirection.Right) movementDetails = matrix.MoveHorizontal(HorizontalMovement.Right); else if (value == InputDirection.Top) movementDetails = matrix.MoveVertical(VerticalMovement.Top); else if (value == InputDirection.Bottom) movementDetails = matrix.MoveVertical(VerticalMovement.Bottom); } if (movementDetails.Count > 0) { StartCoroutine(AnimateItems(movementDetails)); } string x = Utilities.ShowMatrixOnConsole(matrix); DebugDisplay(x); } } Trong phương pháp Cập nhật, ta kiểm tra xem người dùng cung cấp cho ta thông tin đầu vào chưa Nếu vậy, ta thực thao tác vuốt liên quan thu thập danh sách MovementDetails, chứa hoạt ảnh tiềm phải thực Nếu danh sách có ơ, đặt phương thức Animate Items IEnumerator AnimateItems(IEnumerable movementDetails) { List objectsToDestroy = new List(); foreach (var item in movementDetails) { //calculate the new position in the world space var newGoPosition = new Vector3(item.NewColumn + item.NewColumn * distance, item.NewRow + item.NewRow * distance, ZIndex); //move it there var tween = item.GOToAnimatePosition.transform.positionTo(Globals.AnimationDuratio n, newGoPosition); tween.autoRemoveOnComplete = true; AnimateItems bắt đầu cách lặp qua danh sách MovementDetails Sau đó, chuyển đến vị trí khơng gian giới //the scale is != null => this means that this item will also move and duplicate if (item.GOToAnimateScale != null) { var duplicatedItem = matrix[item.NewRow, item.NewColumn]; UpdateScore(duplicatedItem.Value); //check if the item is 2048 => game has ended if (duplicatedItem.Value == 2048) { gameState = GameState.Won; yield return new WaitForEndOfFrame(); } //create the duplicated item var newGO = Instantiate(GetGOBasedOnValue(duplicatedItem.Value), newGoPosition, Quaternion.identity) as GameObject; //make it small in order to animate it newGO.transform.localScale = new Vector3(0.01f, 0.01f, 0.01f); newGO.transform.scaleTo(Globals.AnimationDuration, 1.0f); //assign it to the proper position in the array matrix[item.NewRow, item.NewColumn].GO = newGO; Nếu ô danh sách moveDetails có giá trị khác rỗng trường GOToAnimateScale, điều có nghĩa đối tượng di chuyển sau biến Vì vậy, phương pháp cập nhật điểm số kiểm tra giá trị mới, nhân đơi Điều có 2048, điều có nghĩa trị chơi kết thúc Nếu không, tạo ô nhân bản, làm nhỏ gán vào vị trí thích hợp mảng //we need two animations to happen in chain //first, the movement animation var moveTween = new GoTween(item.GOToAnimateScale.transform, Globals.AnimationDuration, new GoTweenConfig().position(newGoPosition)); //then, the scale one var scaleTween = new GoTween(item.GOToAnimateScale.transform, Globals.AnimationDuration, new GoTweenConfig().scale(0.1f)); var chain = new GoTweenChain(); chain.autoRemoveOnComplete = true; chain.append(moveTween).appendDelay(Globals.AnimationDuration).append(scaleTwe en); chain.play(); //destroy objects after the animations have ended objectsToDestroy.Add(item.GOToAnimateScale); objectsToDestroy.Add(item.GOToAnimatePosition); } } CreateNewItem(); //hold on till the animations finish yield return new WaitForSeconds(Globals.AnimationDuration * movementDetails.Count() * 3); foreach (var go in objectsToDestroy) Destroy(go); Ô hợp nhân di chuyển sau biến Để làm điều này, cần hai hoạt ảnh xảy theo chuỗi, hoạt ảnh chuyển động hoạt ảnh tỷ lệ Ta tạo đối tượng GoTweenChain cho phép ta thực hai hoạt ảnh Ta bắt đầu hoạt ảnh ta thêm hai GameObject hợp vào đối tượngToDestroyList Sau đó, ta CreateNewItem, gọi WaitForSeconds để giữ trình thực thi tất hoạt ảnh dừng lại sau lặp qua danh sách objectsToDestroy để phá hủy tất GameObject không cần thiết Lý ta sử dụng danh sách ta khơng muốn Hủy GameObject hoạt ảnh không thực thi private void UpdateScore(int toAdd) { score += toAdd; ScoreText.text = "Score: " + score; } private GameObject GetGOBasedOnValue(int value) { GameObject newGo = null; switch (value) { case 2: newGo = GO2; break; case 4: newGo = GO4; break; case 8: newGo = GO8; break; case 16: newGo = GO16; break; case 32: newGo = GO32; break; case 64: newGo = GO64; break; case 128: newGo = GO128; break; case 256: newGo = GO256; break; case 512: newGo = GO512; break; case 1024: newGo = GO1024; break; default: throw new System.Exception("Uknown value:" + value); } return newGo; } Phương thức UpdateScore cập nhật điểm số hiển thị hình phương thức GetGOBasedOnValue trả nhà lắp ghép cụ thể để phản hồi đối số giá trị Ví dụ: giá trị 2, phương thức trả GameObject cài sẵn cho giá trị Plugins: - GoKit: để giúp xen khung (tweening), giúp cho việc di chuyển Game Object trở nên mượt mà Phân chia công việc: - Nguyễn Đức Quang: Nguyễn Tùng Lâm: Nguyễn Chí Hiếu: ... phải thực - GameManager: public enum GameState { Playing, Won } public class GameManager : MonoBehaviour { private GameState gameState = GameState.Playing; ItemArray matrix; public GameObject... item.NewColumn]; UpdateScore(duplicatedItem.Value); //check if the item is 2048 => game has ended if (duplicatedItem.Value == 2048) { gameState = GameState.Won; yield return new WaitForEndOfFrame(); } //create... muốn Hủy GameObject hoạt ảnh không thực thi private void UpdateScore(int toAdd) { score += toAdd; ScoreText.text = "Score: " + score; } private GameObject GetGOBasedOnValue(int value) { GameObject

Ngày đăng: 21/12/2021, 18:30

TỪ KHÓA LIÊN QUAN

TÀI LIỆU CÙNG NGƯỜI DÙNG

TÀI LIỆU LIÊN QUAN

w