在不同的场景中使用变量

时间:2017-03-07 17:09:18

标签: swift sprite-kit

在我的GameScene中,我有一个名为“得分”的变量,我想在MenuScene中使用它来显示最新得分。我已经用UserDefaults尝试过了,但我并没有真正理解它并且它不起作用。有人可以告诉我如何声明得分变量,以便我可以在其他场景中使用它,如果有人能告诉我如何保存变量以便我可以保存高分,那也很好。

我试过这个:

GameScene:

let userDefaults = UserDefaults.standard // Before didMove func
UserDefaults.setValue(score, forKey: "Score") // in gameOver func

MenuScene:

var score = SKLabelNode()
var scoreNumber = UserDefaults.standard.integer(forKey: "Score")

如果我运行游戏,它会加载MenuScene,我可以按下播放,然后进入GameScene。如果我死在GameScene中,游戏崩溃并且我收到此错误:“因未捕获的异常而终止应用程序'NSUnknownKeyException',原因:'[setValue:forUndefinedKey:]:此类不是键值编码兼容的关键分数'。“

4 个答案:

答案 0 :(得分:1)

如果您正确使用用户默认值,您应该能够从任何对象读取和写入,因为UserDefaults.standard是单身。这些方面应该有用:

private let scoreUserDefaultsKey = "scoreUserDefaultsKey"

func save(score: Int)
{
    let userDefaults = UserDefaults.standard
    userDefaults.set(score, forKey: scoreUserDefaultsKey)
    userDefaults.synchronize()
}

func read() -> Int
{
    return UserDefaults.standard.integer(forKey: scoreUserDefaultsKey)
}

class GameScene1: SKScene {

    func saveFromGameScene1(score: Int) {
        save(score: score)
    }
}

class GameScene2: SKScene {

    func readFromGameScene2() -> Int {
        return read()
    }
}

说你可以拥有一个更好的架构来实现一个ScoreManager对象并将它注入游戏场景中(而不是对单例有很多依赖)。

<强>更新 刚看到你的代码片段。您需要使用set,而不是setValuesetValue不是UserDefaults的方法,而是KVO的NSObject方法。

  

func setValue(_ value:Any?,forKey key:String)

     

将给定键指定的接收器的属性设置为给定   值。如果key标识了一对一关系,则关联该对象   由值指定给接收者,与先前相关的无关   对象,如果有的话。给定一个集合对象和一个密钥   识别多对多关系,关联包含在其中的对象   收集到接收器,如果没有先前相关的对象   有任何。

     

setValue:forKey:uses的搜索模式在Accessor中描述   “键值编码编程指南”中的搜索模式。

     

在引用计数环境中,如果实例变量是   直接访问,保留值。

这不是你想要的;)

在致电set之后,请务必致电synchronize以确保该值立即存储。

我希望这会有所帮助。

答案 1 :(得分:0)

您可以将import SpriteKit移至全局,即将其从class GameScene移除,并通过在NSUserDefaultlet defaults = NSUserDefaults() var highScoreNumber = defaults.integerForKey("highScoreSaved") If gameScore > highScoreNumber{ highScoreNumber = gameScore defaults.setInteger(highScoreNumber, forKey: "highScoreSaved") } 之间移动来实现通用。之后,您可以像其他人建议的那样使用 public class Employee{ String firstname; String lastname; //setters and getter goes here }

更新

好吧,对于我的GameOver场景,我通常在课堂上做这样的事情

public class GsonRequest<T> extends Request<T> {
        public final Gson gson = new Gson();
        private final Class<T> clazz;
        private final Map<String, String> headers;
        private final Response.Listener<T> listener;
        private final Object dataIn;

        /**
         * Make a GET request and return a parsed object from JSON.
         *
         * @param url     URL of the request to make
         * @param clazz   Relevant class object, for Gson's reflection
         * @param headers Map of request headers
         */
        public GsonRequest(String url, Class<T> clazz, Map<String, String> headers,
                           Response.Listener<T> listener, Response.ErrorListener errorListener) {
            super(Method.GET, url, errorListener);
            this.clazz = clazz;
            this.headers = headers;
            this.listener = listener;
            this.dataIn = null;
        }

        public GsonRequest(String url, Object dataIn, Class<T> clazz, Map<String, String> headers,
                           Response.Listener<T> listener, Response.ErrorListener errorListener) {
            super(Method.POST, url, errorListener);
            this.dataIn = dataIn;
            this.clazz = clazz;
            this.headers = headers;
            this.listener = listener;

        }

        @Override
        public byte[] getBody() throws AuthFailureError {
            return gson.toJson(dataIn).getBytes();
        }

        @Override
        public Map<String, String> getHeaders() throws AuthFailureError {
            return headers != null ? headers : super.getHeaders();
        }

        @Override
        protected void deliverResponse(T response) {
            listener.onResponse(response);
        }

        @Override
        protected Response<T> parseNetworkResponse(NetworkResponse response) {
            try {
                String json = new String(
                        response.data,
                        HttpHeaderParser.parseCharset(response.headers));
                return Response.success(
                        gson.fromJson(json, clazz),
                        HttpHeaderParser.parseCacheHeaders(response));
            } catch (UnsupportedEncodingException e) {
                return Response.error(new ParseError(e));

            }
        }
    }

您可以在MenuScene中执行此操作。

答案 2 :(得分:0)

如果你想在差异viewcontroller中使用动态变量。并且一直使用它。你在Appdelegate中创建。

在你的AppDelegate.swift

    var yourVariableName: Any = "Value"

当您想要使用它或更改它时。你

    let app: AppDelegate = UIApplication.shared.delegate as! AppDelegate
    print(app.youVariableName)

如果您想观察变量值的变化并做一些动作。您可以设置didSet功能。

var yourVariableName: Any = "VALUE" {didSet{observeValueChangeFunction()}}
func didSet{observeValueChangeFunction() {
     print("New value: \(yourVariableName)")
}

答案 3 :(得分:0)

我认为您不应该使用UserDefaults来执行此操作,

我想问你的第一个原因是,如果有人关闭应用程序,也许他们在玩游戏时会杀死应用程序,你是如何与得分达成交易的?

当然,你不应该保留这个分数,对吧?或者你应该缓存游戏的所有数据,然后当玩家再次进入游戏时,你可以回到应用程序关闭时的状态。但我认为这样做太复杂了。

所以我认为你应该把这个分数全局或更容易控制,这只是将分数从GameScene传递给可能被称为ResultScene的分数。

我认为你的游戏有这些场景: LoginScene MapScene GameScene ResultScene RankScene

所以你只需实时使用GameSceneResultScene中的分数,所以你只需在GameScene中执行此操作:

var score   : Int/CGFloat     = 0;
func enterFrame(){
   //if they got the goal, then
   // score = score + 1 or much more;
}

func gameOver(){
   let resultScene = ResultScene(score: score);
   // or you could just create the empty ResultScene, and pass the score via a public func of the ResultScene
}

然后您可以在ResultScene中显示分数。

如果您只是制作无服务器游戏,则应将分数保留在UserDefaultsCoreDataSQLite等。 然后你可以在一些关于history records的场景中显示分数。