SceneKit框架詳細(xì)解析(三) —— 基于SceneKit的簡(jiǎn)單游戲示例的實(shí)現(xiàn)(二)

版本記錄

版本號(hào) 時(shí)間
V1.0 2018.10.20 星期六

前言

SceneKit使用高級(jí)場(chǎng)景描述創(chuàng)建3D游戲并將3D內(nèi)容添加到應(yīng)用程序。 輕松添加動(dòng)畫(huà),物理模擬,粒子效果和逼真的基于物理性的渲染。接下來(lái)這幾篇我們就詳細(xì)的解析一下這個(gè)框架。感興趣的看下面幾篇文章。
1. SceneKit框架詳細(xì)解析(一) —— 基本概覽(一)
2. SceneKit框架詳細(xì)解析(二) —— 基于SceneKit的簡(jiǎn)單游戲示例的實(shí)現(xiàn)(一)

開(kāi)始

在這部分中,您將開(kāi)始制作游戲,順路學(xué)習(xí)Scene Kit節(jié)點(diǎn)。

我們?cè)倩貋?lái)吧!

在上一個(gè)教程中,您了解到SceneKit將游戲組件組織為一個(gè)稱為scene graph的層次結(jié)構(gòu)。

游戲的每個(gè)元素 - 例如燈光,照相機(jī),幾何體和粒子發(fā)射器 - 都稱為節(jié)點(diǎn)nodes,節(jié)點(diǎn)存儲(chǔ)在這種樹(shù)狀結(jié)構(gòu)中。

為了說(shuō)明這是如何工作的,回想一下你可能聽(tīng)過(guò)的童年童謠......

?? The hip bone’s connected to the back bone ?? The back bone’s connected to the shoulder bone… ??

你是對(duì)的! 這是經(jīng)典歌曲Dem Dry Bones。 如果你知道一個(gè)特別好用這個(gè)概念的經(jīng)典視頻游戲,那么獎(jiǎng)勵(lì)積分。

考慮到這些歌詞,請(qǐng)看一下罕見(jiàn)的四指骨架的以下解剖學(xué)上正確的結(jié)構(gòu):

為了幫助說(shuō)明如何從此骨架構(gòu)建基于節(jié)點(diǎn)的層次結(jié)構(gòu),請(qǐng)將骨架中的每個(gè)骨骼視為節(jié)點(diǎn)node

正如歌曲指出的那樣,肩骨與背骨相連。因此,將后骨骼視為肩骨的父節(jié)點(diǎn),將肩骨骨骼視為后骨骼的子節(jié)點(diǎn)。

要將肩骨添加到場(chǎng)景中,請(qǐng)將其添加為后骨骼的子項(xiàng)。您可以繼續(xù)以這種方式構(gòu)建整個(gè)手臂,將子骨骼添加到父骨骼。

要定位骨骼,請(qǐng)相對(duì)于其父骨骼定位骨骼。例如,要揮動(dòng)骨架的左臂,只需按照小藍(lán)箭頭的指示來(lái)回旋轉(zhuǎn)肩節(jié)點(diǎn)即可。肩節(jié)點(diǎn)的所有子節(jié)點(diǎn)將與其父節(jié)點(diǎn)一起旋轉(zhuǎn)。

恭喜!你剛剛通過(guò)骨骼解剖學(xué)!

從技術(shù)角度來(lái)看,單個(gè)節(jié)點(diǎn)由SCNNode類表示,并表示相對(duì)于其父節(jié)點(diǎn)在3D空間中的位置。節(jié)點(diǎn)本身沒(méi)有可見(jiàn)內(nèi)容,因此,當(dāng)作為場(chǎng)景的一部分呈現(xiàn)時(shí),它是不可見(jiàn)的。要?jiǎng)?chuàng)建可見(jiàn)內(nèi)容,您必須向節(jié)點(diǎn)添加其他組件,如燈光,相機(jī)或幾何圖形(如骨骼)。

場(chǎng)景圖(scene graph)包含一個(gè)特殊節(jié)點(diǎn),它構(gòu)成了基于節(jié)點(diǎn)的層次結(jié)構(gòu)的基礎(chǔ):根節(jié)點(diǎn)。要構(gòu)建場(chǎng)景,可以將節(jié)點(diǎn)添加為根節(jié)點(diǎn)的子節(jié)點(diǎn),也可以添加為根節(jié)點(diǎn)后代之一的子節(jié)點(diǎn)。

在本教程中,您將開(kāi)始使用SceneKit中的一些簡(jiǎn)單節(jié)點(diǎn),例如相機(jī)和幾何節(jié)點(diǎn)。在本教程結(jié)束時(shí),您將在屏幕上呈現(xiàn)一個(gè)簡(jiǎn)單的3D立方體!


Asset Catalogs

一旦你成為一名成功且富有經(jīng)驗(yàn)的3D游戲設(shè)計(jì)師,你將有足夠的資金聘請(qǐng)你自己的圖形藝術(shù)家和音響工程師,這將讓你自由地專注于游戲代碼。SceneKit asset catalog專門設(shè)計(jì)用于幫助您與代碼分開(kāi)管理游戲資產(chǎn)。

通過(guò)資產(chǎn)目錄,您可以在單個(gè)文件夾中管理游戲資產(chǎn)。 要使用它,只需將帶有.scnassets擴(kuò)展名的文件夾添加到項(xiàng)目中,并將所有游戲資源保存在該文件夾中。 Xcode會(huì)在構(gòu)建時(shí)將目錄中的所有內(nèi)容復(fù)制到您的應(yīng)用包中。 Xcode保留您的資產(chǎn)文件夾層次結(jié)構(gòu);這使您可以完全控制文件夾結(jié)構(gòu)。

通過(guò)與您的藝術(shù)家共享您的資產(chǎn)文件夾,他們可以快速解決任何問(wèn)題,例如一個(gè)不那么可怕的跨眼怪物,并為下一次構(gòu)建做好準(zhǔn)備,而無(wú)需將更改的資產(chǎn)復(fù)制回項(xiàng)目。

1. Adding an Asset Catalog - 添加資產(chǎn)目錄

現(xiàn)在您已了解資產(chǎn)目錄的全部?jī)?nèi)容,您將向Geometry Fighter添加一個(gè)。

GeometryFighter.scnassets文件夾從resources文件夾拖放到Xcode中的游戲項(xiàng)目中。 在出現(xiàn)的彈出窗口中,確保選中Copy items if needed,Create GroupsGeometryFighter目標(biāo),然后單擊Finish。

在項(xiàng)目導(dǎo)航器中選擇GeometryFighter.scnassets文件夾。 請(qǐng)注意右側(cè)面板中資產(chǎn)目錄的其他獨(dú)有設(shè)置。 展開(kāi)GeometryFighter.scnassets文件夾和子文件夾以查看有關(guān)資產(chǎn)的更多詳細(xì)信息:

資產(chǎn)目錄中有兩個(gè)文件夾:

  • Sounds:包含游戲所需的所有聲音資源。
  • Textures:包含您需要的所有圖像。

隨意偷看每個(gè)文件夾里面的內(nèi)容。

2. Adding a Launch Screen - 添加啟動(dòng)屏幕

現(xiàn)在您已導(dǎo)入資產(chǎn)目錄,您將負(fù)責(zé)一些基本的內(nèi)部處理步驟,包括在啟動(dòng)屏幕上添加適當(dāng)?shù)膱D像。

首先,單擊項(xiàng)目導(dǎo)航器中的Assets.xcassets。 將GeometryFighter.scnassets / Textures / Logo_Diffuse.png拖放到AppIcon下面的資源中。

接下來(lái),單擊項(xiàng)目導(dǎo)航器中的LaunchScreen.storyboard。 選擇主視圖并將Background屬性設(shè)置為深藍(lán)色:

接下來(lái),將Logo_Diffuse圖像從Media Library拖動(dòng)到視圖的中心。 將新圖像的Content Mode屬性設(shè)置為Aspect Fit

你的啟動(dòng)屏幕差不多完成了。 您需要做的就是添加一些約束,以便啟動(dòng)圖像適用于所有設(shè)備。 單擊底部的Pin按鈕,切換所有四條邊的約束,然后單擊Add 4 Constraints,如下所示:

您已完成設(shè)置啟動(dòng)屏幕! 構(gòu)建并運(yùn)行您的應(yīng)用程序; 你會(huì)看到你閃亮的新發(fā)布屏幕出現(xiàn):

3. Adding a Background Image - 添加背景圖像

一旦你的啟動(dòng)畫(huà)面消失,你就會(huì)被轉(zhuǎn)回空白的屏幕。 是時(shí)候添加一個(gè)漂亮干凈的背景,這樣你就不會(huì)覺(jué)得自己正盯著一個(gè)黑洞。

為此,請(qǐng)將以下代碼行添加到GameViewController.swift中setupScene()的底部:

scnScene.background.contents = "GeometryFighter.scnassets/Textures/Background_Diffuse.png"

此行代碼指示場(chǎng)景從資產(chǎn)目錄中加載Background_Diffuse.png圖像,并將其用作場(chǎng)景背景的材質(zhì)屬性。

構(gòu)建并運(yùn)行;你現(xiàn)在應(yīng)該在游戲開(kāi)始時(shí)看到藍(lán)色背景圖像:

您已完成項(xiàng)目的所有基本內(nèi)部處理任務(wù)。 你的游戲現(xiàn)在有一個(gè)華麗的應(yīng)用程序圖標(biāo),一個(gè)啟動(dòng)畫(huà)面和漂亮的背景,它們都準(zhǔn)備好顯示你將要添加到場(chǎng)景中的節(jié)點(diǎn)。


The SceneKit Coordinate System - SceneKit坐標(biāo)系

在開(kāi)始向場(chǎng)景添加節(jié)點(diǎn)之前,首先需要了解SceneKit的坐標(biāo)系如何工作,以便將節(jié)點(diǎn)定位在所需位置。

在諸如UIKit或SpriteKit的2D系統(tǒng)中,您使用一個(gè)點(diǎn)來(lái)描述x和y軸上的視圖或sprite的位置。 要在3D空間中放置對(duì)象,還需要描述對(duì)象在z軸上的位置深度。
請(qǐng)考慮以下簡(jiǎn)單說(shuō)明:

SceneKit使用這個(gè)三軸系統(tǒng)來(lái)表示3D空間中的位置。 紅色塊沿x軸放置,綠色塊沿y軸放置,藍(lán)色塊沿z軸放置。 軸正中心的灰色立方體表示原點(diǎn),坐標(biāo)為(x:0,y:0,z:0)

SceneKit使用SCNVector3數(shù)據(jù)類型將三維坐標(biāo)表示為三分量向量。 以下是在代碼中創(chuàng)建向量的方法:

let position = SCNVector3(x: 0, y: 5, z: 10)

這將使用向量(x:0,y:5,z:10)聲明position位置。 您可以輕松訪問(wèn)矢量的各個(gè)屬性,如下所示:

let x = position.x
let y = position.y
let z = position.z

如果您之前使用過(guò)CGPoint,則可以輕松地在它與SCNVector3之間進(jìn)行比較。

注意:添加到場(chǎng)景中的節(jié)點(diǎn)的默認(rèn)位置為(x:0,y:0,z:0),它始終相對(duì)于父節(jié)點(diǎn)。 要將節(jié)點(diǎn)放置在所需位置,您需要調(diào)整節(jié)點(diǎn)相對(duì)于其父節(jié)點(diǎn)的位置(本地坐標(biāo)) - 而不是原點(diǎn)(世界坐標(biāo))。


Cameras - 相機(jī)

既然您已經(jīng)了解了如何在SceneKit中定位節(jié)點(diǎn),那么您可能想知道如何在屏幕上實(shí)際顯示某些內(nèi)容。 回想一下本教程系列第1部分中電影集的類比。 要拍攝場(chǎng)景,您需要放置一個(gè)觀察場(chǎng)景的攝像機(jī),并且從攝像機(jī)的角度來(lái)看該場(chǎng)景的結(jié)果圖像。

SceneKit以類似的方式工作;包含攝像機(jī)的節(jié)點(diǎn)的位置決定了您從中查看場(chǎng)景的視點(diǎn)。

下圖演示了攝像機(jī)在SceneKit中的工作原理:

上圖中有幾個(gè)關(guān)鍵點(diǎn):

  • 1) 攝像機(jī)的視線方向始終沿著包含攝像機(jī)的節(jié)點(diǎn)的負(fù)z軸。
  • 1) 視野( field of view)是相機(jī)可視區(qū)域的限制角度。 緊密的角度提供了狹窄的視野,而廣角提供了寬闊的視野。
  • 1) 視錐體(viewing frustum)確定了相機(jī)的可見(jiàn)深度。 此區(qū)域外的任何東西 - 即距離相機(jī)太近或太遠(yuǎn) - 都將被剪裁,不會(huì)出現(xiàn)在屏幕上。

SceneKit攝像機(jī)由SCNCamera表示,其xPovyPov屬性可讓您調(diào)整視野,而zNearzFar可讓您調(diào)整視錐體。

要記住的一個(gè)關(guān)鍵點(diǎn)是攝像機(jī)本身不會(huì)做任何事情,除非它是節(jié)點(diǎn)層次結(jié)構(gòu)的一部分。

1. Adding a Camera - 添加相機(jī)

是時(shí)候嘗試一下了。 打開(kāi)GameViewController.swift并在scnScene下面添加以下屬性:

var cameraNode: SCNNode!

接下來(lái),在setupScene()下面添加以下方法:

func setupCamera() {
  // 1
  cameraNode = SCNNode()
  // 2
  cameraNode.camera = SCNCamera()
  // 3
  cameraNode.position = SCNVector3(x: 0, y: 0, z: 10)
  // 4
  scnScene.rootNode.addChildNode(cameraNode)
}

仔細(xì)看看代碼:

  • 1) 首先,創(chuàng)建一個(gè)空的SCNNode并將其分配給cameraNode。
  • 2) 接下來(lái),您將創(chuàng)建一個(gè)新的SCNCamera對(duì)象并將其分配給cameraNodecamera屬性。
  • 3) 然后,將攝像機(jī)的位置設(shè)置為(x:0,y:0,z:10)
  • 4) 最后,將cameraNode添加到場(chǎng)景中,作為場(chǎng)景根節(jié)點(diǎn)的子節(jié)點(diǎn)。

通過(guò)調(diào)用viewDidLoad()中剛剛添加的方法來(lái)完成任務(wù),就在setupScene()下面:

setupCamera()

沒(méi)有必要構(gòu)建和運(yùn)行。 即使你剛剛在場(chǎng)景中添加了一個(gè)攝像頭,仍然沒(méi)有什么可看的,這意味著你不會(huì)看到任何不同的東西。 但那即將改變!


Geometry - 幾何

要?jiǎng)?chuàng)建可見(jiàn)內(nèi)容,您需要將幾何對(duì)象添加到節(jié)點(diǎn)。 幾何對(duì)象表示三維形狀,并且由稱為定義多邊形polygonsvertices的許多點(diǎn)創(chuàng)建。

此外,幾何對(duì)象可以包含修改幾何體表面外觀的材質(zhì)對(duì)象。 通過(guò)材質(zhì),您可以指定幾何體表面的顏色和紋理等信息,以及幾何體應(yīng)如何響應(yīng)光線以及其他視覺(jué)效果。 頂點(diǎn)和材質(zhì)的集合稱為模型或網(wǎng)格(model 或者 mesh)。

SceneKit包含以下內(nèi)置幾何形狀:

在前排,從左側(cè)開(kāi)始,您有一個(gè)圓錐,一個(gè)圓環(huán),一個(gè)膠囊和一個(gè)管子。 在后排,從左側(cè)開(kāi)始,你有一個(gè)金字塔,一個(gè)盒子,一個(gè)球體和一個(gè)圓柱體。

1. Adding ShapeTypes - 添加ShapeTypes

在開(kāi)始向場(chǎng)景添加幾何形狀之前,請(qǐng)創(chuàng)建一個(gè)新的Swift文件,以定義您將在游戲中使用的不同形狀的ShapeType枚舉。

右鍵單擊GeometryFighter組并選擇New File ....選擇iOS / Source / Swift File文件模板,然后單擊Next

將文件命名為ShapeType.swift,確保它包含在項(xiàng)目中,然后單擊Create。

創(chuàng)建文件后,打開(kāi)ShapeType.swift并使用以下內(nèi)容替換其內(nèi)容:

import Foundation

// 1
enum ShapeType:Int {

  case box = 0
  case sphere
  case pyramid
  case torus
  case capsule
  case cylinder
  case cone  case tube

  // 2
  static func random() -> ShapeType {
    let maxValue = tube.rawValue
    let rand = arc4random_uniform(UInt32(maxValue+1))
    return ShapeType(rawValue: Int(rand))!
  }
}

上面的代碼相對(duì)簡(jiǎn)單:

  • 1) 您創(chuàng)建一個(gè)名為ShapeType的新枚舉,枚舉各種形狀。
  • 2) 您還定義了一個(gè)名為random()的靜態(tài)方法,該方法生成隨機(jī)ShapeType。 此功能將在您的游戲中稍后派上用場(chǎng)。

2. Adding a Geometry Node - 添加幾何節(jié)點(diǎn)

您的下一個(gè)任務(wù)是創(chuàng)建一個(gè)方法,該方法生成ShapeType中定義的各種隨機(jī)形狀。

將以下方法添加到GameViewController.swift,就在setupCamera()下面:

func spawnShape() {
  // 1
  var geometry:SCNGeometry
  // 2
  switch ShapeType.random() {
  default:
    // 3
    geometry = SCNBox(width: 1.0, height: 1.0, length: 1.0,
      chamferRadius: 0.0)
  }  
  // 4
  let geometryNode = SCNNode(geometry: geometry)
  // 5
  scnScene.rootNode.addChildNode(geometryNode)
}

依次記錄每個(gè)編號(hào)的評(píng)論:

  • 1) 首先,創(chuàng)建占位符geometry變量以供稍后使用。
  • 2) 接下來(lái),定義一個(gè)switch語(yǔ)句來(lái)處理ShapeType.random()返回的形狀。 目前它還不完整,只創(chuàng)造了一個(gè)盒子形狀;在本教程結(jié)束時(shí),您將在挑戰(zhàn)中添加更多內(nèi)容。
  • 3) 然后,創(chuàng)建一個(gè)SCNBox對(duì)象并將其存儲(chǔ)在geometry中。 您可以指定寬度,高度和長(zhǎng)度以及倒角半徑(這是一種說(shuō)明圓角的奇特方式)。
  • 4) 在這里,您將創(chuàng)建名為geometryNodeSCNNode實(shí)例。 這次,您使用SCNNode初始化程序,它使用geometry參數(shù)來(lái)創(chuàng)建節(jié)點(diǎn)并自動(dòng)附加提供的幾何體。
  • 5) 最后,將節(jié)點(diǎn)添加為場(chǎng)景根節(jié)點(diǎn)的子節(jié)點(diǎn)。

現(xiàn)在你需要調(diào)用這個(gè)方法。 將以下行添加到setupCamera()下面的viewDidLoad()

spawnShape()

構(gòu)建并運(yùn)行;你會(huì)看到屏幕上顯示一個(gè)白色方塊:

這里有幾點(diǎn)需要注意:

  • 1) box節(jié)點(diǎn)是spawnShape()的默認(rèn)形狀,它位于場(chǎng)景中的(x:0,y:0,z:0)
  • 2) 您正在通過(guò)cameraNode查看場(chǎng)景。 由于攝像機(jī)節(jié)點(diǎn)位于(x:0,y:0:z:10),因此該box位于攝像機(jī)可視區(qū)域的中心。

好吧,這不是很令人興奮,而且它幾乎不是三維的 - 但不要害怕......下一部分會(huì)改變所有這些!


Built-in View Features - 內(nèi)置視圖功能

SCNView具有一些開(kāi)箱即用的功能,可幫助您輕松生活。

將以下行添加到GameViewController.swift中的setupView(),就在當(dāng)前實(shí)現(xiàn)的下方:

// 1
scnView.showsStatistics = true
// 2
scnView.allowsCameraControl = true
// 3
scnView.autoenablesDefaultLighting = true

以下是對(duì)上述代碼的解釋:

  • 1) showStatistics在場(chǎng)景底部啟用實(shí)時(shí)統(tǒng)計(jì)面板。
  • 2) allowsCameraControl允許您通過(guò)簡(jiǎn)單的手勢(shì)手動(dòng)控制活動(dòng)相機(jī)。
  • 3) autoenablesDefaultLighting在場(chǎng)景中創(chuàng)建一個(gè)通用的全向燈,因此您不必?fù)?dān)心添加自己的光源。

構(gòu)建并運(yùn)行;這次事情應(yīng)該看起來(lái)更令人興奮!

您可以使用以下手勢(shì)來(lái)控制場(chǎng)景中的活動(dòng)相機(jī):

  • Single finger swipe - 單指滑動(dòng):圍繞場(chǎng)景內(nèi)容旋轉(zhuǎn)活動(dòng)攝像機(jī)。
  • Two finger swipe - 雙指滑動(dòng):在場(chǎng)景中向左,向右,向上或向下移動(dòng)或平移相機(jī)。
  • 雙指捏 - Two finger pinch:將相機(jī)放入和移出場(chǎng)景。
  • 雙擊 - Double-tap:如果您有多個(gè)攝像頭,則會(huì)在場(chǎng)景中的攝像頭之間切換。 當(dāng)然,由于場(chǎng)景中只有一臺(tái)攝像機(jī),因此不會(huì)這樣做。 但是,它還具有將相機(jī)重置為其原始位置和設(shè)置的效果。

1. Working with Scene Statistics - 使用場(chǎng)景統(tǒng)計(jì)

找到屏幕底部的統(tǒng)計(jì)信息面板:

以下是每個(gè)元素含義的快速細(xì)分:

  • fps:代表每秒幀數(shù)。這是在一秒內(nèi)完成的連續(xù)幀重繪總量的測(cè)量。這個(gè)數(shù)量越低,你的游戲表現(xiàn)就越差。您通常希望您的游戲以60fps的速度運(yùn)行,這將使您的游戲看起來(lái)更加流暢。
  • ◆:代表每幀的總繪圖調(diào)用。這通常是每個(gè)幀繪制的可見(jiàn)對(duì)象的總量。影響對(duì)象的燈光也會(huì)增加對(duì)象的繪制調(diào)用量。這個(gè)數(shù)量越低越好。
  • ▲:代表每幀的總多邊形。這是用于為所有可見(jiàn)幾何體繪制單個(gè)幀的多邊形總數(shù)。這個(gè)數(shù)量越低越好。
  • ?:代表全部可見(jiàn)光源。這是當(dāng)前影響可見(jiàn)對(duì)象的光源總量。 SceneKit指南建議一次不要使用3個(gè)以上的光源。

單擊+按鈕展開(kāi)面板并顯示更多詳細(xì)信息:

此面板為您提供以下信息:

  • Frame time - 幀時(shí)間:這是繪制單幀所花費(fèi)的總時(shí)間。 需要16.7ms的幀時(shí)間來(lái)實(shí)現(xiàn)60fps的幀速率。
  • The color chart: - 顏色圖表:這為您提供了SceneKit渲染管道中每個(gè)組件的粗略幀時(shí)間百分比細(xì)分。

從這個(gè)例子中,您現(xiàn)在知道繪制一個(gè)幀的時(shí)間為22.3ms,其中±75%用于渲染,±25%用于GL Flush。

注意:稍后將更詳細(xì)地討論SceneKit渲染管道。

您可以單擊-按鈕以再次最小化面板。

所有這些功能都是內(nèi)置是不是很好?


Challenges - 挑戰(zhàn)

對(duì)您來(lái)說(shuō),練習(xí)您自己學(xué)到的知識(shí)非常重要,本系列的許多部分都有一個(gè)或多個(gè)與之相關(guān)的挑戰(zhàn)。

我強(qiáng)烈建議嘗試所有的挑戰(zhàn)。雖然按照分步教程進(jìn)行,但您可以通過(guò)自己解決問(wèn)題來(lái)學(xué)習(xí)更多知識(shí)。

1. Your First Challenge - 你的第一個(gè)挑戰(zhàn)

本教程中只有一個(gè)挑戰(zhàn),但它很有趣。

您的挑戰(zhàn)是改進(jìn)spawnShape()中的switch語(yǔ)句以處理枚舉器中的其余形狀。

使用Apple的官方SceneKit文檔(http://apple.co/2aDBgtH)作為各種幾何形狀的指南。另外,看一下ShapeType枚舉,看看要?jiǎng)?chuàng)建哪些形狀;他們的名字應(yīng)該讓你知道從哪里開(kāi)始。

不要過(guò)分擔(dān)心使用的尺寸;試著讓它們與你之前制作的盒子大小相同。

在這次挑戰(zhàn)之后,您將牢牢掌握SceneKit中的一些最基本的概念!

后記

本篇主要講述了基于SceneKit的簡(jiǎn)單游戲示例的實(shí)現(xiàn),感興趣的給個(gè)贊或者關(guān)注~~~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容