首页 > 其他分享 >[SpriteKit] 制作瓦片地图小游戏

[SpriteKit] 制作瓦片地图小游戏

时间:2023-06-08 19:03:09浏览次数:38  
标签:Int SpriteKit 小游戏 let func 瓦片 var open


概述


SpriteKit制作瓦片地图游戏,深入了解2D游戏制作过程



说实话这个2D游戏实战的入门看的我脑浆子都沸腾了, 好多新的概念涌入, 没做过游戏开发的我表示真的难以接受, 吸收效率与之前相比也下降好多, 不过越往后学, 就能够加深对之前知识的掌握, 这可能也是看书的好处吧, 今天我也把对瓦片地图的一些学习经验记录下来供大家探讨.

[SpriteKit] 制作瓦片地图小游戏_瓦片地图

说实话, 我很推荐Ray家的资源, 由浅入深手把手的教学, 内容前后呼应, 看几本书就能涵盖国内4个月培训班的课程体系. 遵循本系列一贯的风格, 我们还是从基础的API开始看起, 对API掌握熟练的话, 多敲两个Demo就能够基本的上手任何项目了.

一、瓦片地图技术要点

1、SKTileMapNode

@available(iOS 10.0, *)
open class SKTileMapNode : SKNode, NSCopying, NSCoding
     public init(tileSet: SKTileSet, columns: Int, rows: Int, tileSize: CGSize)
     open var numberOfColumns: Int
     open var numberOfRows: Int
     open var tileSize: CGSize
     open var mapSize: CGSize { get }
     open var tileSet: SKTileSet
     open var colorBlendFactor: CGFloat
     open func tileDefinition(atColumn column: Int, row: Int) -> SKTileDefinition?
     open func tileGroup(atColumn column: Int, row: Int) -> SKTileGroup?
     open func setTileGroup(_ tileGroup: SKTileGroup?, forColumn column: Int, row: Int)
     open func tileColumnIndex(fromPosition position: CGPoint) -> Int
     open func tileRowIndex(fromPosition position: CGPoint) -> Int
     open func centerOfTile(atColumn column: Int, row: Int) -> CGPoint
  • init(tileSet: SKTileSet, columns: Int, rows: Int, tileSize: CGSize) 瓦片地图节点的初始化方法
  • numberOfColumns 瓦片地图的列数
  • numberOfRows 瓦片地图的行数
  • tileSize 瓦片地图中每个瓦片的尺寸
  • mapSize 瓦片地图的尺寸
  • tileSet 瓦片地图的瓦片集
  • colorBlendFactor 瓦片的渲染着色
  • tileDefinition(atColumn column: Int, row: Int) -> SKTileDefinition? 根据列数和行数返回瓦片定义
  • tileGroup(atColumn column: Int, row: Int) -> SKTileGroup? 根据列数和行数返回瓦片组
  • setTileGroup(_ tileGroup: SKTileGroup?, forColumn column: Int, row: Int) 根据列数和行数设置瓦片组
  • tileColumnIndex(fromPosition position: CGPoint) -> Int 根据瓦片位置返回瓦片在瓦片地图中列数下标
  • tileRowIndex(fromPosition position: CGPoint) -> Int 根据瓦片位置返回瓦片在瓦片地图中行数下标

2、SKTileSet

@available(iOS 10.0, *)
open class SKTileSet : NSObject, NSCopying, NSCoding 
     public init(tileGroups: [SKTileGroup])
     open var tileGroups: [SKTileGroup]
     open var name: String?
     open var defaultTileGroup: SKTileGroup?
     open var defaultTileSize: CGSize
     open var type: SKTileSetType

@available(iOS 10.0, *)
public enum SKTileSetType : UInt {
    case grid
    case isometric
    case hexagonalFlat
    case hexagonalPointy
}
  • init(tileGroups: [SKTileGroup]) 根据瓦片组初始化瓦片集
  • tileGroups 瓦片组
  • name 瓦片集的标识
  • defaultTileGroup 瓦片集默认瓦片组
  • defaultTileSize 瓦片集默认瓦片尺寸
  • type 瓦片集类型 - 网格, 等值, 六边形

3、SKTileGroup

@available(iOS 10.0, *)
open class SKTileGroup : NSObject, NSCopying, NSCoding
     open class func empty() -> Self
     public init(tileDefinition: SKTileDefinition)
     public init(rules: [SKTileGroupRule])
     open var name: String?
  • empty() 返回一个空的瓦片组
  • init(tileDefinition: SKTileDefinition) 根据瓦片定义初始化瓦片组
  • init(rules: [SKTileGroupRule]) 根据瓦片组规则初始化瓦片组
  • name 瓦片组的标识

4、SKTileGroupRule

@available(iOS 10.0, *)
open class SKTileGroupRule : NSObject, NSCopying, NSCoding
     public init(adjacency: SKTileAdjacencyMask, tileDefinitions: [SKTileDefinition])
     open var adjacency: SKTileAdjacencyMask
     open var tileDefinitions: [SKTileDefinition]
     open var name: String?
  • init(adjacency: SKTileAdjacencyMask, tileDefinitions: [SKTileDefinition]) 根据瓦片链接和瓦片定义初始化瓦片组规则
  • adjacency 瓦片链接
  • tileDefinitions 瓦片规则
  • name 瓦片组规则标识

5、SKTileDefinition

@available(iOS 10.0, *)
open class SKTileDefinition : NSObject, NSCopying, NSCoding
     public init(texture: SKTexture)
     public init(textures: [SKTexture], normalTextures: [SKTexture], size: CGSize, timePerFrame: CGFloat)
     open var userData: NSMutableDictionary?
     open var name: String?
     open var size: CGSize
     open var timePerFrame: CGFloat
     open var rotation: SKTileDefinitionRotation
     open var flipVertically: Bool
     open var flipHorizontally: Bool
  • init(texture: SKTexture) 根据纹理初始化瓦片定义
  • init(textures: [SKTexture], normalTextures: [SKTexture], size: CGSize, timePerFrame: CGFloat) 根据纹理集合, 尺寸, 和帧率初始化瓦片定义
  • userData 瓦片定义的用户数据
  • name 瓦片定义的标识
  • timePerFrame 瓦片定义的帧率
  • rotation 瓦片定义的旋转规则
  • flipVertically 是否垂直翻转
  • flipHorizontally 是否水平翻转

二、程序实现

API, 了解一些基本的就够了, 如果要深究可以打开头文件逐个尝试, 我们现在就来实现一个小游戏, 这个游戏中包含了3个场景, 控制人物在规定时间内消灭所有的害虫, 我们着手进行游戏的开发吧!

[SpriteKit] 制作瓦片地图小游戏_制作瓦片地图小游戏_02

1、step1 设置游戏场景的属性

class GameScene: SKScene {
  var background: SKTileMapNode! //背景瓦片地图节点
  var obstaclesTileMap: SKTileMapNode? //障碍物瓦片地图节点
  var bugsprayTileMap: SKTileMapNode? //杀虫喷剂瓦片地图节点

  var bugsNode = SKNode() //害虫的节点

  var player = Player() //玩家的节点
  var hud = HUD() //文字说明

  var firebugCount: Int = 0 //高级害虫的节点数

  var timeLimit: Int = 10 //时间限制
  var elapsedTime: Int = 0 //经过时间
  var startTime: Int? //开始时间

  var currentLevel: Int = 1 //当前关卡等级

  var gameState: GameState = .initial { //游戏状态默认为初始状态
    didSet { 
      hud.updateGameState(from: oldValue, to: gameState) //更新游戏状态
    }
  }
  ...
}

2、step2 加载游戏场景的初始化设置

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    background =
      childNode(withName: "background") as! SKTileMapNode //通过节点名读取背景瓦片地图节点
    obstaclesTileMap = childNode(withName: "obstacles") 
      as? SKTileMapNode //通过节点名读取障碍物瓦片地图节点

    if let timeLimit =
      userData?.object(forKey: "timeLimit") as? Int {
      self.timeLimit = timeLimit //通过节点的用户数据设置每个场景的时间限制
    }
    // 1
    let savedGameState = aDecoder.decodeInteger(forKey: "Scene.gameState") //解档保存游戏状态
    if let gameState = GameState(rawValue: savedGameState), gameState == .pause { //当解档保存游戏状态为暂停时
      self.gameState = gameState //赋值游戏状态
      firebugCount = aDecoder.decodeInteger(
        forKey: "Scene.firebugCount") //解档高级害虫数
      elapsedTime = aDecoder.decodeInteger(
        forKey: "Scene.elapsedTime") //解档经过时间
      currentLevel = aDecoder.decodeInteger(
        forKey: "Scene.currentLevel") //解档当前关卡等级
      // 2
      player = childNode(withName: "Player") as! Player //根据节点名读取玩家节点
      hud = camera!.childNode(withName: "HUD") as! HUD //根据节点名读取文字说明
      bugsNode = childNode(withName: "Bugs")! //根据节点名读取害虫节点
      bugsprayTileMap = childNode(withName: "Bugspray") 
        as? SKTileMapNode //通过节点名读取杀虫喷雾瓦片地图节点
    }

    addObservers() //添加观察者
  }

  deinit {
    NotificationCenter.default.removeObserver(self) //移除观察者
  }

3、step3 当场景移动到屏幕时的设置

override func didMove(to view: SKView) {
    if gameState == .initial { //当游戏状态为初始状态时
      addChild(player) //添加玩家到场景
      setupWorldPhysics() //添加物理世界
      createBugs() //添加害虫
      setupObstaclePhysics() //添加障碍物
      if firebugCount > 0 { //如果有高级害虫
        createBugspray(quantity: firebugCount + 10) //添加杀虫喷雾
      }
      setupHUD() //添加文字说明
      gameState = .start //设置游戏状态为开始状态
    }
    setupCamera() //添加摄像头
  }

4、step4 物理世界的设置

func setupWorldPhysics() {
    background.physicsBody =
      SKPhysicsBody(edgeLoopFrom: background.frame) //设置边缘物理体
    background.physicsBody?.categoryBitMask = PhysicsCategory.Edge //设置物理体标识为边缘

    physicsWorld.contactDelegate = self //物理世界代理

  }

5、step5 创建害虫的设置

func createBugs() {
    guard let bugsMap = childNode(withName: "bugs")
      as? SKTileMapNode else { return } //校验害虫瓦片地图节点
    // 1
    for row in 0..<bugsMap.numberOfRows { //逐行遍历害虫瓦片地图
      for column in 0..<bugsMap.numberOfColumns { //逐列遍历害虫瓦片地图
        // 2
        guard let tile = tile(in: bugsMap,
                              at: (column, row)) 
          else { continue } //校验瓦片地图中的每个瓦片
        // 3
        let bug: Bug 
        if tile.userData?.object(forKey: "firebug") != nil { //从用户数据中判断是否为高级害虫
          bug = Firebug() //将害虫设置为高级害虫
          firebugCount += 1 //高级害虫书自增
        } else {
          bug = Bug() //将害虫设置为普通害虫
        }
        bug.position = bugsMap.centerOfTile(atColumn: column,
                                            row: row) //从害虫瓦片地图中读取位置并赋值
        bugsNode.addChild(bug) //添加节点
        bug.moveBug() //移动害虫
      }
    }
    // 4
    bugsNode.name = "Bugs" //设置害虫节点标识
    addChild(bugsNode) //添加父节点到场景
    // 5
    bugsMap.removeFromParent() //删除害虫瓦片地图地图节点
  }

6、step6 添加障碍物的设置

func setupObstaclePhysics() {
    guard let obstaclesTileMap = obstaclesTileMap else { return } //校验障碍物瓦片地图节点
    // 1
    for row in 0..<obstaclesTileMap.numberOfRows {
      for column in 0..<obstaclesTileMap.numberOfColumns {
        // 2
        guard let tile = tile(in: obstaclesTileMap,
                              at: (column, row))
          else { continue }
        guard tile.userData?.object(forKey: "obstacle") != nil
          else { continue }
        // 3
        let node = SKNode() //创建节点
        node.physicsBody = SKPhysicsBody(rectangleOf: tile.size) 根据瓦片尺寸创建物理体
        node.physicsBody?.isDynamic = false //不进入物理世界
        node.physicsBody?.friction = 0 //摩擦系数为0
        node.physicsBody?.categoryBitMask =
          PhysicsCategory.Breakable //设置物理体标识

        node.position = obstaclesTileMap.centerOfTile(
          atColumn: column, row: row)
        obstaclesTileMap.addChild(node)
      }
    }
  }

7、step7 添加杀虫喷雾的设置

func createBugspray(quantity: Int) {
    // 1
    let tile = SKTileDefinition(texture:
      SKTexture(pixelImageNamed: "bugspray")) //创建瓦片定义
    // 2
    let tilerule = SKTileGroupRule(adjacency:
      SKTileAdjacencyMask.adjacencyAll, tileDefinitions: [tile]) //创建瓦片组规则
    // 3 
    let tilegroup = SKTileGroup(rules: [tilerule]) //创建瓦片组
    // 4
    let tileSet = SKTileSet(tileGroups: [tilegroup]) //创建瓦片集

    // 5
    let columns = background.numberOfColumns //读取背景瓦片地图节点的列数
    let rows = background.numberOfRows //读取背景瓦片地图节点的行数
    bugsprayTileMap = SKTileMapNode(tileSet: tileSet, 
                                    columns: columns,
                                    rows: rows,
                                    tileSize: tile.size) //创建新的瓦片地图节点
    // 6
    for _ in 1...quantity { 
      let column = Int.random(min: 0, max: columns-1) //随机列数
      let row = Int.random(min: 0, max: rows-1) //随机行数
      bugsprayTileMap?.setTileGroup(tilegroup,
                                    forColumn: column, row: row) //在新额的瓦片地图节点上随机生成瓦片组
    }
    // 7
    bugsprayTileMap?.name = "Bugspray" //设置瓦片地图节点的标识
    addChild(bugsprayTileMap!) //将瓦片地图添加到场景

  }

8、step8 添加摄像头设置

func setupCamera() {
    guard let camera = camera, let view = view else { return }

    let zeroDistance = SKRange(constantValue: 0)
    let playerConstraint = SKConstraint.distance(zeroDistance,
                                                 to: player) //对玩家进行约束
    // 1
    let xInset = min(view.bounds.width/2 * camera.xScale,
                     background.frame.width/2)
    let yInset = min(view.bounds.height/2 * camera.yScale,
                     background.frame.height/2)

    // 2
    let constraintRect = background.frame.insetBy(dx: xInset,
                                                  dy: yInset)
    // 3
    let xRange = SKRange(lowerLimit: constraintRect.minX,
                         upperLimit: constraintRect.maxX)
    let yRange = SKRange(lowerLimit: constraintRect.minY,
                         upperLimit: constraintRect.maxY)

    let edgeConstraint = SKConstraint.positionX(xRange, y: yRange)
    edgeConstraint.referenceNode = background
    // 4
    camera.constraints = [playerConstraint, edgeConstraint]
  }

9、step9 获取瓦片的一些帮助方法

func tile(in tileMap: SKTileMapNode,
            at coordinates: TileCoordinates)
    -> SKTileDefinition? {
      return tileMap.tileDefinition(atColumn: coordinates.column,
                                    row: coordinates.row)
  }

  func tileCoordinates(in tileMap: SKTileMapNode,
                       at position: CGPoint) -> TileCoordinates {
    let column = tileMap.tileColumnIndex(fromPosition: position)
    let row = tileMap.tileRowIndex(fromPosition: position)
    return (column, row)
  }

  func tileGroupForName(tileSet: SKTileSet, name: String)
    -> SKTileGroup? {
      let tileGroup = tileSet.tileGroups
        .filter { $0.name == name }.first
      return tileGroup
  }

10、step10 点击场景的设置

override func touchesBegan(_ touches: Set<UITouch>,
                             with event: UIEvent?) {
    guard let touch = touches.first else { return }
    switch gameState {
    // 1
    case .start: //开始状态
      gameState = .play //切换成游戏状态
      isPaused = false //开始
      startTime = nil
      elapsedTime = 0
    // 2
    case .play: //游戏状态
      player.move(target: touch.location(in: self)) //移动玩家
    case .win: //获胜状态
      transitionToScene(level: currentLevel + 1) //切换场景
    case .lose: //落败状态
      transitionToScene(level: 1) //切换场景
    case .reload: //唤醒状态
      // 1
      if let touchedNode =
        atPoint(touch.location(in: self)) as? SKLabelNode {
        // 2
        if touchedNode.name == HUDMessages.yes { //如果点击的节点是YES
          isPaused = false
          startTime = nil
          gameState = .play
          // 3
        } else if touchedNode.name == HUDMessages.no { //如果点击的节点是NO
          transitionToScene(level: 1)
        }
      }
    default:
      break
    }
  }

11、step11 切换场景的设置

func transitionToScene(level: Int) {
    // 1
    guard let newScene = SKScene(fileNamed: "Level\(level)")
      as? GameScene else {
        fatalError("Level: \(level) not found")
    }
    // 2
    newScene.currentLevel = level
    view!.presentScene(newScene,
                       transition: SKTransition.flipVertical(withDuration: 0.5))
  }

12、step12 刷帧

override func update(_ currentTime: TimeInterval) {
    if gameState != .play  {
      isPaused = true //如果不是游戏状态就暂停刷帧
      return
    }

    if !player.hasBugspray {
      updateBugspray() //如果玩家没有杀虫喷雾, 就进行更新
    }
    advanceBreakableTile(locatedAt: player.position) //更新障碍物的物理体状态
    updateHUD(currentTime: currentTime) //更新文字说明
    checkEndGame() //检查是否达到胜负条件
  }

13、step13 更新杀虫喷雾

func updateBugspray() {
    guard let bugsprayTileMap = bugsprayTileMap else { return }
    let (column, row) = tileCoordinates(in: bugsprayTileMap,
                                        at: player.position)
    if tile(in: bugsprayTileMap, at: (column, row)) != nil {
      bugsprayTileMap.setTileGroup(nil, forColumn: column,
                                   row: row)
      player.hasBugspray = true
    }
  }

14、step14 更新障碍物的物理体状态

func advanceBreakableTile(locatedAt nodePosition: CGPoint) {
    guard let obstaclesTileMap = obstaclesTileMap else { return }
    // 1
    let (column, row) = tileCoordinates(in: obstaclesTileMap,
                                        at: nodePosition)
    // 2
    let obstacle = tile(in: obstaclesTileMap,
                        at: (column, row))
    //3
    guard let nextTileGroupName =
      obstacle?.userData?.object(forKey: "breakable") as? String
      else { return }
    // 4
    if let nextTileGroup =
      tileGroupForName(tileSet: obstaclesTileMap.tileSet,
                       name: nextTileGroupName) {
      obstaclesTileMap.setTileGroup(nextTileGroup, 
                                    forColumn: column, row: row) //设置新的瓦片组到瓦片地图中
    }
  }

15、step15 更新文字说明

func updateHUD(currentTime: TimeInterval) {
    // 1
    if let startTime = startTime {
      // 2
      elapsedTime = Int(currentTime) - startTime
    } else {
      // 3
      startTime = Int(currentTime) - elapsedTime
    }
    // 4
    hud.updateTimer(time: timeLimit - elapsedTime) //对文字说明进行更新
  }

16、step16 检查是否达到胜负条件

func checkEndGame() {
    if bugsNode.children.count == 0 { //是否消灭全部害虫
      player.physicsBody?.linearDamping = 1
      gameState = .win
    } else if timeLimit - elapsedTime <= 0 { //是否时间用完
      player.physicsBody?.linearDamping = 1
      gameState = .lose
    }
  }

17、step17 物理世界代理的设置

extension GameScene : SKPhysicsContactDelegate {
  func remove(bug: Bug) { //消灭害虫
    bug.removeFromParent()
    background.addChild(bug)
    bug.die()
    hud.updateBugCount(with: bugsNode.children.count)
  }

  func didBegin(_ contact: SKPhysicsContact) {
    let other = contact.bodyA.categoryBitMask
      == PhysicsCategory.Player ?
        contact.bodyB : contact.bodyA

    switch other.categoryBitMask {
    case PhysicsCategory.Bug:
      if let bug = other.node as? Bug {
        remove(bug: bug) //当玩家接触到普通害虫, 消灭普通害虫
      }
    case PhysicsCategory.Firebug:
      if player.hasBugspray {
        if let firebug = other.node as? Firebug {
          remove(bug: firebug)
          player.hasBugspray = false //当玩家手持杀虫喷雾接触高级害虫才能消灭高级害虫
        }
      }
    case PhysicsCategory.Breakable:
      if let obstacleNode = other.node {
        // 1
        advanceBreakableTile(locatedAt: obstacleNode.position) //更新障碍物
        // 2
        obstacleNode.removeFromParent() //删除原障碍物
      }

    default:
      break
    }
    if let physicsBody = player.physicsBody {
      if physicsBody.velocity.length() > 0 {
        player.checkDirection() //进行玩家方向的设置
      }
    }
  }
}

18、step18 观察者的设置

extension GameScene {
  func applicationDidBecomeActive() {
    print("* applicationDidBecomeActive")
    if gameState == .pause {
      gameState = .reload //重新进入, 进行游戏重载
    }
  }

  func applicationWillResignActive() {
    print("* applicationWillResignActive")
    isPaused = true
    if gameState != .lose {
      gameState = .pause  //暂停游戏进程
    }
  }

  func applicationDidEnterBackground() {
    print("* applicationDidEnterBackground")
    if gameState != .lose {
      saveGame() //进入后台保存游戏进度
    }
  }

  func addObservers() {
    NotificationCenter.default.addObserver(self,
                                           selector: #selector(applicationDidBecomeActive),
                                           name: .UIApplicationDidBecomeActive, object: nil)
    NotificationCenter.default.addObserver(self,
                                           selector: #selector(applicationWillResignActive),
                                           name: .UIApplicationWillResignActive, object: nil)
    NotificationCenter.default.addObserver(self,
                                           selector: #selector(applicationDidEnterBackground),
                                           name: .UIApplicationDidEnterBackground, object: nil)
  }

}

19、step19 游戏的存储设置

extension GameScene {
  func saveGame() {
    // 1
    let fileManager = FileManager.default
    guard let directory =
      fileManager.urls(for: .libraryDirectory,
                       in: .userDomainMask).first
      else { return }
    // 2
    let saveURL = directory.appendingPathComponent("SavedGames")
    // 3
    do {
      try fileManager.createDirectory(atPath: saveURL.path,
                                      withIntermediateDirectories: true,
                                      attributes: nil)
    } catch let error as NSError {
      fatalError(
        "Failed to create directory: \(error.debugDescription)")
    }
    // 4
    let fileURL = saveURL.appendingPathComponent("saved-game")
    print("* Saving: \(fileURL.path)")
    // 5
    NSKeyedArchiver.archiveRootObject(self, toFile: fileURL.path) //文件处理器新建路径并归档
  }

  override func encode(with aCoder: NSCoder) { /对关键属性的归档
    aCoder.encode(firebugCount,
                  forKey: "Scene.firebugCount")
    aCoder.encode(elapsedTime,
                  forKey: "Scene.elapsedTime")
    aCoder.encode(gameState.rawValue,
                  forKey: "Scene.gameState")
    aCoder.encode(currentLevel,
                  forKey: "Scene.currentLevel")
    super.encode(with: aCoder)
  }

  class func loadGame() -> SKScene? { //重新加载存储游戏进程
    print("* loading game")
    var scene: SKScene?
    // 1
    let fileManager = FileManager.default
    guard let directory =
      fileManager.urls(for: .libraryDirectory,
                       in: .userDomainMask).first
      else { return nil }
    // 2
    let url = directory.appendingPathComponent(
      "SavedGames/saved-game")
    // 3
    if FileManager.default.fileExists(atPath: url.path) {
      scene = NSKeyedUnarchiver.unarchiveObject(  //根据路径进行解档游戏进程
        withFile: url.path) as? GameScene
      _ = try? fileManager.removeItem(at: url)
    }
    return scene
  }

}

三、运行效果与文件截图

1、运行效果

2、文件截图

[SpriteKit] 制作瓦片地图小游戏_iOS_03

PestControl文件里的截图:

[SpriteKit] 制作瓦片地图小游戏_瓦片地图_04

PestControl.xcodeproj文件里的截图:

[SpriteKit] 制作瓦片地图小游戏_初始化_05

四、其他补充

Notice: 忽略了一些节点的设置, 但不影响瓦片地图的理解.


标签:Int,SpriteKit,小游戏,let,func,瓦片,var,open
From: https://blog.51cto.com/u_7583030/6442280

相关文章

  • 【百行代码说游戏】ActionScript3.0 小游戏 【劲舞团】源码+演示
    最近学ActionScript3.0以下为自己写的一个小游戏。尽量以最少的代码,实现功能游戏原理:看代码注释游戏规则:类似于【劲舞团】游戏,玩家可以按UP,DOWN,LEFT,RIGHT键来操控游戏。打掉相应的箭头则得分,否则失手。箭头落到底线没有被打掉,则为失手。失手10次,游戏结束。得分过30,升一级。难度......
  • 训练简单小游戏的强化学习工具箱
    详细先上效果图:启动界面主界面设置界面服务器界面(使用highchart模板画出每一局得分情况)配置的两款简单小游戏以及训练效果:贪吃蛇“是男人就下一百层”(修改)*原图像太大被迫修改大小使用说明:####【设置窗口】→在上面的主界面中点击倒三角形状的键,屏幕上会弹出一个黑色的设置窗。在......
  • [SpriteKit] 系统框架中Cocos2d-x制作小游戏ZombieConga
    概述使用SpriteKit实现一个简单的游戏,通过一个游戏来进行SpriteKit的入门,熟练2D游戏的API,也可以更好的结合在iOS应用中.详细今天我们进入一个全新的系列,先熟悉SpriteKit,然后再看实战的游戏案例。一、了解SpriteKit本期的内容就是使用SpriteKit实现一个简单的游......
  • 速度挑战 - 2小时完成HTML5拼图小游戏
    概述我用lufylegend.js开发了第一个HTML5小游戏——拼图游戏,还写了篇博文来炫耀一下:HTML5小游戏《智力大拼图》发布,挑战你的思维风暴。详细初学lufylegend.js之日,我用lufylegend.js开发了第一个HTML5小游戏——拼图游戏,还写了篇博文来炫耀一下:HTML5小游戏《智力大......
  • Python猜拳小游戏
    #猜拳小游戏#0:石头;1:剪刀;2:布#账户:yujinhua#密码:123456importrandom#随机数computer=random.randint(0,2)account=str(input("请输入游戏账户:"))password=int(input("请输入游戏密码:"))ifaccount==("yujinhua")andpassword==123456:  print("对局开始&......
  • 常见项目——C语言实现2048小游戏(附源码和可执行文件)
    1.实现效果展示1.1界面图片说明:进入下面各页面后均可通过按键“q”来退出。1)菜单页2)开始游戏页3)游戏规则页4)按键说明页5)退出游戏页1.2视频展现戳此处查看视频演示1.3游戏源码及可执行文件(exe)下载支持作者,请关注公众号“优秀物联人”回复“2048小游戏”获取下......
  • c++拼图小游戏
    文件目录结构  common.h#pragmaonce#include<iostream>#include<graphics.h>//图形库#include<array>//定长数组#include<algorithm>//乱序算法#include<functional>//仿函数#include<ctime>//时间time函数的头文件usingnamespacestd;//窗口属性类//窗口的宽和高#define......
  • 有趣的前端小游戏
    今天搬了一天的家,状态不是很好,分享一个小游戏给大家吧~先介绍一下游戏规则:1、点击play后,游戏会提供4中不同的昆虫供玩家进行选择,这四种昆虫分别为:蟑螂、蛛蛛、蚊子和苍蝇。2、当玩家选择完昆虫后,游戏开始3、游戏会随机的生成玩家选择的昆虫到电脑屏幕,玩家利用鼠标点击到则得一分......
  • 一个简单的 rust 项目 使用 bevy 引擎 复刻 Flappy Bird 小游戏
    Rust+Bevy实现的FlappyBird游戏简介一个使用bevy引擎复刻的FlappyBird经典小游戏。通过该项目我们可以学到:bevy的自定义组件,自定义插件,自定义资源,sprite的旋转,sprite的移动,spritesheet动画的定义使用,状态管理,等内容…简单介绍一下包含的内容:游戏状态管理Me......
  • 基于SuperMap iDesktop制作栅格和矢量瓦片
    栅格瓦片的制作与使用 栅格瓦片的生产瓦片地图缓存可以提升客户端地图呈现的显示效率,但是生成瓦片地图缓存也是一项耗费时间的工作。桌面产品生成缓存iServer分布式切图SuperMapUGC缓存SuperMapUGCV5缓存MongoDB分布式存储缓存MongoDB栅格瓦片的制作(一)......