[SpriteKit] 制作瓦片地图小游戏
概述
详细
说实话这个2D游戏实战的入门看的我脑浆子都沸腾了, 好多新的概念涌入, 没做过游戏开发的我表示真的难以接受, 吸收效率与之前相比也下降好多, 不过越往后学, 就能够加深对之前知识的掌握, 这可能也是看书的好处吧, 今天我也把对瓦片地图的一些学习经验记录下来供大家探讨.
说实话, 我很推荐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个场景, 控制人物在规定时间内消灭所有的害虫, 我们着手进行游戏的开发吧!
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、文件截图
PestControl文件里的截图:
PestControl.xcodeproj文件里的截图:
四、其他补充
Notice: 忽略了一些节点的设置, 但不影响瓦片地图的理解.
注:本文著作权归作者,由demo大师发表,拒绝转载,转载需要作者授权
[SpriteKit] 制作瓦片地图小游戏的更多相关文章
- 用C#制作推箱子小游戏
思路分析: 一.制作一个地图 二.地图中放置墙.箱子.人.目标等 三.让小人动起来完成推箱子动作 游戏制作: 1.按照上述地图制作一个地图 (12行×13列) 地图可以看做是行和列组成的,即可以看做 ...
- 制作动画或小游戏——CreateJS事件(二)
在Canvas中如果要添加事件,就需要计算坐标来模拟各种事件,而EaselJS中已经封装好了多个事件,只需调用即可. 一.事件 1)点击 事件是绑定在Shape类中的,click事件与DOM中的意思是 ...
- 制作一个 JavaScript 小游戏
简评: 作者学习了编程两个月,边学边做了一个 JavaScript 小游戏,在文中总结了自己在这个过程中的一些体会,希望能给其他初学者一些帮助. 对于很多想学编程但一直没下定决心的同学来说,最大的问题 ...
- 制作动画或小游戏——CreateJS基础类(一)
前面曾经记录过Canvas的基础知识<让自己也能使用Canvas>,在实际使用中,用封装好的库效率会高点. 使用成熟的库还能对基础知识有更深入的理解,CreateJS是基于HTML5开发的 ...
- IOS学习之路五(SpriteKit 开发飞机大战小游戏一)
参考SpriteKit 创建游戏的教程今天自己动手做了一下,现在记录一下自己怎么做的,今天之做了第一步,一共有三个部分. 第一步,项目搭建. 项目所用图片资源:点击打开链接 1.在Xcode打开之后, ...
- Python制作塔防小游戏
开发工具 Python版本:3.6.4 相关模块: pygame模块: 以及一些Python自带的模块.
- 原生JS制作贪吃蛇小游戏
感情都在代码里,来,干了!... <!doctype html> <html> <head> <meta http-equiv="Content-T ...
- 用cocos2d 2.1制作一个过河小游戏(4): 游戏主逻辑BaseLayer设计
前段时间一直在忙.没有时间更新博客.今天还是抽点时间把最后一小部分游戏的实现放上来吧. BaseLayer.h: #import <GameKit/GameKit.h> #import & ...
- 利用python 5分钟制作一款小游戏
1.安装pygame 在命令行cmd中输入:pip install pygame ( 注:如果安装不成功,需要输入:python -m pip install --user --upgrade pip ...
随机推荐
- Unity中一键创建常用文件夹
Unity中一键创建常用文件夹 说明 项目测试版本Unity5.3. 这个一个小工具:功能非常简单,就是一键给新建工程添加所有文件夹.到此结束. 但是具体咋操作呢? 与把大象装进冰箱一样,三步,下载代 ...
- 4. python 修改字符串实例总结
4. python 修改字符串实例总结 我们知道python里面字符串是不可原处直接修改的,为了是原来的字符串修改过来,我们有一下方法: 1.分片和合并 >>> a='abcde' ...
- SWT 全接触
http://www.ibm.com/developerworks/cn/opensource/os-swt/index.html 1.SWT简介 SWT-"Standard Widget ...
- 【centOS7】centOS7上普通用户切换root用户,相互切换
当前普通用户登录,想要切换为root用户,需要输入命令 su 需要输入root密码.输入时候屏幕不会显示,直接输入完了,回车即可 回车后,即切换到root用户下 想要从root用户切换到普通用户,只需 ...
- C# 输入法 z
C# 输入法 虽说输入法不是什么新事物,各种语言版本都有,不过在C#不常见:这就会给人一种误会:C#不能做!其实C#能不能做呢,答案是肯定的——三种方式都行:IMM.TSF以及外挂式.IMM这种就是调 ...
- AFNetworking 2.0使用(持续更新)
本人视频教程系列 导入AFNetworking 2.0 文件夹,引入头文件AFNetworking.h --------------- *使用NSURLSessionDownloadTask来下载一张 ...
- Mantis的config_inc.php的各配置项的作用及其修改
Mantis的设置是这样保存的:在 config_defaults_inc.php中保存Mantis的默认设置,用户自己的设置信息保存在config_inc.php中.如果某个选项在config_in ...
- 将MyEclipse项目导入到Eclipse中
1.请首先确保你的eclipse是javaee版本的,或者已经安装wtp插件 2.然后修改eclipse工程下的.project文件: 3.在<natures></natures&g ...
- Coursera课程《大家的Python》中一些资料
Printed copies of Python for Informatics are available for $10 or less from Amazon and $2 or less on ...
- 第十二章 ThreadPoolExecutor使用 + 工作机理 + 生命周期
1.最基础的线程池ThreadPoolExecutor 使用方式: /** * ThreadPoolExecutor测试类 * 注意: * 1.ThreadPoolExecutor是一个线程池 * 2 ...