AgentsCatalog是Apple的一个例子,他是基于OC的,我认为重写这些例子是个学习语言的好方法。首先,你的目标是正确的,通过观摩源码,你知道如何达到目标,其次你所使用的手段也是正确的,你可以从中学到某个类的用法,几个类的相互关系等等。
前几天我一直纠结一个事情,我打算把工具条合并到窗口的标题栏上去,就像Safari那样,我查了很多资料一直没能解决,但是这个例子无意间解决了这个问题。而且只有一行代码。你建立一个Cocoa应用,就像下面这样。
- @NSApplicationMain
- class AppDelegate: NSObject,NSApplicationDelegate {
- @IBOutlet weak var window: NSWindow!
- @IBOutlet weak var skView: SKView!
- @IBOutlet weak var sceneControl: NSSegmentedControl!
- @IBAction func selectScene(sender: NSSegmentedControl) {
- let sceneTYpe = AAPLSceneType(rawValue: sender.selectedSegment)
- let scene = AAPLGameScene.sceneWithType( sceneTYpe!,size: CGSizeMake(800,600))
- scene.scaleMode = SKSceneScaleMode.AspectFit
- self.skView.presentScene(scene)
- }
- func applicationDidFinishLaunching(aNotification: NSNotification) {
- // Insert code here to initialize your application
- self.window.titleVisibility = NSWindowTitleVisibility.Hidden
- // Configure the view.
- self.skView.ignoresSiblingOrder = true
- self.skView.showsFPS = true
- self.skView.showsNodeCount = true
- // Present the scene.
- self.selectScene(self.sceneControl )
- }
- func applicationWillTerminate(aNotification: NSNotification) {
- // Insert code here to tear down your application
- }
你的工具条就合并到window标题栏上去了。其他什么也不用做。接下来你要重写下面这个类:
这是所有后面用到的 Scene 类基类,他本身是SKSence的子类。你会看到 SKComponentSystem(组件系统)和 GKAgent2D(跟踪代理)的用法。这里处理了游戏事件循环和对鼠标的响应。
- import Cocoa
- import SpriteKit
- import GameplayKit
- enum AAPLSceneType: Int {
- case AAPLSceneTypeSeek = 0,AAPLSceneTypeWander,AAPLSceneTypeFlee,AAPLSceneTypeAvoid,AAPLSceneTypeSeparate,AAPLSceneTypeAlign,AAPLSceneTypeFlock,AAPLSceneTypePath,AAPLSceneTypesCount
- }
- let AAPLDefaultAgentRadius: CGFloat = 40.0
- class AAPLGameScene: SKScene {
- var sceneName: String?
- required override init(size: CGSize) {
- super.init(size: size)
- }
- required init?(coder aDecoder: NSCoder) {
- fatalError("init(coder:) has not been implemented")
- }
- // A component system to manage per-frame updates for all agents.
- var agentSystem: GKComponentSystem?
- // An agent whose position tracks that of mouseDragged (OS X) or touchesMoved (iOS) events.
- // This agent has no display representation,but can be used to make other agents follow the mouse/touch.
- var trackingAgent: GKAgent2D?
- // YES when the mouse is dragging (OS X) or a touch is moving
- var seeking: Bool = false
- var stopGoal: GKGoal?
- var lastUpdateTime: NSTimeInterval = 0
- class func sceneWithType( sceneType: AAPLSceneType,size: CGSize) -> AAPLGameScene {
- var sceneClass: AAPLGameScene.Type
- switch (sceneType) {
- case AAPLSceneType.AAPLSceneTypeSeek:
- sceneClass = AAPLSeekScene.self
- break;
- case AAPLSceneType.AAPLSceneTypeWander:
- sceneClass = AAPLWanderScene.self
- break;
- case AAPLSceneType.AAPLSceneTypeFlee:
- sceneClass = AAPLFleeScene.self
- break;
- case AAPLSceneType.AAPLSceneTypeAvoid:
- sceneClass = AAPLAvoidScene.self
- break;
- case AAPLSceneType.AAPLSceneTypeSeparate:
- sceneClass = AAPLSeparateScene.self
- break;
- case AAPLSceneType.AAPLSceneTypeAlign:
- sceneClass = AAPLAlignScene.self
- break;
- case AAPLSceneType.AAPLSceneTypeFlock:
- sceneClass = AAPLFlockScene.self
- break;
- case AAPLSceneType.AAPLSceneTypePath:
- sceneClass = AAPLPathScene.self
- break;
- default:
- sceneClass = AAPLGameScene.self
- break;
- }
- return sceneClass.init(size: CGSizeMake(800,600))
- }
- override func didMoveToView(view: SKView) {
- self.agentSystem = GKComponentSystem( componentClass: GKAgent2D.self )
- self.trackingAgent = GKAgent2D()
- let x = CGRectGetMidX(self.frame)
- let y = CGRectGetMidY(self.frame)
- self.trackingAgent!.position = vector_float2( Float(x),Float(y))
- }
- override func update(currentTime: NSTimeInterval) {
- // Calculate delta since last update and pass along to the agent system.
- if (lastUpdateTime == 0) {
- lastUpdateTime = currentTime;
- }
- let delta = currentTime - lastUpdateTime
- lastUpdateTime = currentTime
- self.agentSystem!.updateWithDeltaTime( delta )
- }
- override func mouseDown(theEvent: NSEvent) {
- self.seeking = true
- }
- override func mouseUp(theEvent: NSEvent) {
- self.seeking = false
- }
- override func mouseDragged(theEvent: NSEvent) {
- let position = theEvent.locationInNode(self)
- self.trackingAgent!.position = vector_float2 (Float(position.x),Float(position.y))
- }
- }
- import Cocoa
- import SpriteKit
- import GameplayKit
- class AAPLAgentNode: SKNode,GKAgentDelegate{
- var agent = GKAgent2D()
- var color: SKColor = SKColor.highlightColor()
- var drawsTrail: Bool = false
- var triangleShape: SKShapeNode!
- var particles: SKEmitterNode!
- var defaultParticleRate: CGFloat = 0
- init( scene: SKScene,radius: CGFloat,position: CGPoint ){
- super.init()
- self.position = position;
- self.zPosition = 10;
- scene.addChild(self)
- // An agent to manage the movement of this node in a scene.
- agent = GKAgent2D()
- agent.radius = Float(radius)
- agent.position = (vector_float2)( Float(position.x),Float(position.y))
- agent.delegate = self;
- agent.maxSpeed = 100;
- agent.maxAcceleration = 50;
- // A circle to represent the agent's radius in the agent simulation.
- let circleShape = SKShapeNode(circleOfRadius: radius)
- circleShape.lineWidth = 2.5
- circleShape.fillColor = SKColor.grayColor()
- circleShape.zPosition = 1
- self.addChild(circleShape)
- // A triangle to represent the agent's heading (rotation) in the agent simulation.
- var points = Array(count: 4,repeatedValue: CGPoint())
- let triangleBackSideAngle = CGFloat((135.0 / 360.0) * (2 * M_PI))
- points[0] = CGPointMake(radius,0); // Tip.
- points[1] = CGPointMake(radius * cos(triangleBackSideAngle),radius * sin(triangleBackSideAngle)); // Back bottom.
- points[2] = CGPointMake(radius * cos(triangleBackSideAngle),-radius * sin(triangleBackSideAngle)); // Back top.
- points[3] = CGPointMake(radius,0); // Back top.
- let triangleShape = SKShapeNode(points: &points,count: 4)
- triangleShape.lineWidth = 2.5;
- triangleShape.zPosition = 1;
- self.addChild( triangleShape)
- // A particle effect to leave a trail behind the agent as it moves through the scene.
- particles = SKEmitterNode(fileNamed: "Trail.sks")
- defaultParticleRate = particles!.particleBirthRate;
- particles!.position = CGPointMake(-radius + 5,0);
- particles!.targetNode = scene;
- particles!.zPosition = 0;
- self.addChild( particles!)
- }
- required init?(coder aDecoder: NSCoder) {
- fatalError("init(coder:) has not been implemented")
- }
- // GKAgentDelegate
- func agentWillUpdate(agent: GKAgent){
- }
- /*
- * Called after [GKAgent updateWithDeltaTime:] is called each frame.
- */
- func agentDidUpdate(agent: GKAgent){
- // Agent and sprite use the same coordinate system (in this app),// so just convert vector_float2 position to CGPoint.
- let agent2D = agent as! GKAgent2D
- self.position = CGPointMake( CGFloat(agent2D.position.x),CGFloat(agent2D.position.y))
- self.zRotation = CGFloat(agent2D.rotation)
- }
- }
- //
- // AAPLWanderScene.swift
- // TestAgents
- //
- // Created by wuzhiqiang on 15/11/14.
- // Copyright © 2015年 wuzhiqiang. All rights reserved.
- //
- import Cocoa
- import SpriteKit
- import GameplayKit
- class AAPLWanderScene: AAPLGameScene {
- override func didMoveToView(view: SKView) {
- super.didMoveToView(view)
- let pt = CGPointMake(CGRectGetMidX(self.frame),CGRectGetMidY(self.frame))
- let wanderer = AAPLAgentNode( scene: self,radius: 40,position: pt )
- wanderer.color = SKColor.cyanColor()
- wanderer.agent.behavior = GKBehavior( goal: GKGoal( toWander:10),weight: 100)
- self.agentSystem!.addComponent( wanderer.agent)
- }
- }
下面我们来描述一下程序的工作过程。
在AppDelegate里面,我们建立一个Scence,8个中的一个,这里是AAPLWanderScene,漫游型。
这个 AAPLWanderScene 建立一个自己的 Node (一艘船),然后建立并将 GKBehavior 对象赋给了 节点的agent.behavior 属性。
最后,将这个节点作为一个组件加入到组件系统。
就是这样。程序就会运行,组件系统会工作,定时循环更新他的每个组件。你的小船会自动出发漫游。
在节点里,甚至还加入了粒子效果,你能看到小船后面的水泡。