SpriteKit - Preloader using GCD - objective-c

I am looking for the instructions of how to make an preloader using GCD, but no luck for now. I would like to learn how to preload all the textures and emitters in the background, before scene is initialized. And if possible, update the progress bar animation based on amount of data loaded.
I am looking at Apple's adventure game, and there is an example for similar problem. Actually, the first part is solved. But I can't find anywhere about updating preloader based on the amount of data loaded, so any instructions would be helpful. There must be some obvious way,to calculate, or get info about data loaded in the background queue.

I don't do any progress bars since my levels take about a second to load. I have a seamless transition between levels. Before I was doing things this way my game would freeze for a moment in a transition because it had to load all the resources on the same thread that my game was running
I create a static method in my class to preload resources.
// config is a struct with many parameters
class func createResources(config: HyperSceneConfig, withCompletion: (scene: HyperScene) -> ()){
// load resources on other thread
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), {
let hyperScene = HyperScene(config: config)
// callback on main thread
dispatch_async(dispatch_get_main_queue(), {
// Call the completion handler back on the main queue.
withCompletion(scene: hyperScene)
});
})
}
my scenes init
init(config: HyperSceneConfig) {
super.init(size: config.size, viewController: config.viewController)
self.backgroundColor = SKColor.blackColor()
self.levelTime = 200
// this is where i do all my setup before the scene is presented
self.setupScene()
}
this is how I present my scene in my gameViewController.
You could do the same sort of thing in a scene to load the next level. This is great because you can create a seamless transition between levels since everything is loading on a different thread.
HyperScene.createResources(hyperConfig, withCompletion: {
scene in
self.scene = scene
self.scene.scaleMode = .AspectFill
self.skView.presentScene(self.scene)
self.view.addSubview(self.skView)
})

Related

Can I preload the web content for Safari View Controller?

I can create Safari View Controller without problem:
let svc = SFSafariViewController(URL: NSURL(string: remote_url)!, entersReaderIfAvailable: true)
self.presentViewController(svc, animated: true, completion: nil)
Is there any way I can preload the URL before I present the view controller to the user?
For example, I can preload the URL (web content) in the background first, and after the user clicks on something, I can show the Safari View Controller with the content right away. The user will feel the page loading is faster or instant.
P.S. Workarounds/hacks are also acceptable. For example, using cache or starting the view controller in background, etc.
EDIT: please consider SFSafariViewController only.
Here is a solution.
Obviously, if you click on the button right away you'll see the loading.
But basically, I load the Browser and put the view behind another one and I put a button in this other view.
When you press the button, the browser is bring to the front, already loaded.
The only problem here is that I'm not using any transition but that's one solution at least.
import UIKit
import SafariServices
class ViewController: UIViewController {
var svc = SFSafariViewController(URL: NSURL(string: "https://microsoft.com/")!, entersReaderIfAvailable: true)
var safariView:UIView?
let containerView = UIView()
let btn = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
//let tmpView = svc.view
addChildViewController(svc)
svc.didMoveToParentViewController(self)
svc.view.frame = view.frame
containerView.frame = view.frame
containerView.backgroundColor = UIColor.redColor()
safariView = svc.view
view.addSubview(safariView!)
view.addSubview(containerView)
btn.setTitle("Webizer", forState: UIControlState.Normal)
btn.titleLabel!.textColor = UIColor.blackColor()
btn.addTarget(self, action: "buttonTouched:", forControlEvents: .TouchUpInside)
btn.frame = CGRectMake(20, 50, 100, 100)
containerView.addSubview(btn)
view.sendSubviewToBack(safariView!)
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func buttonTouched(sender: AnyObject) {
view.bringSubviewToFront(safariView!)
//self.presentViewController(svc, animated: true, completion: nil)
}
}
Sadly this behaviour is not supported with the current implementation of SFSafariViewController. I would encourage filing a radar with Apple to add support for this behaviour but like others have suggested your best bet is to use WKWebView and start loading before its added to the hierarchy.
I came across a lovely radar from Twitter that actually mentions exactly what you're asking for. I think you might find the following requests useful:
High Priority:
- Ability to warm the SFSafariViewController before actually presenting it with a URL, URL request, HTML data or file on disk
- Currently, are investing heavily into warming the shared URL cache for high priority Tweets so that if the user hits that Tweet we
will open UIWebView (sadly not WKWebView) with that pre-cached web
page. If we could just warm an SFSafariViewController with the
desired link, this would eliminate an enormous amount of effort on our
end.
You can see in their implementation they simply cache responses using UIWebView since WKWebView seems to obfuscate the caching semantics a bit. The only risk is that UIWebView is a likely candidate for deprecation as you see in their docs "In apps that run in iOS 8 and later, use the WKWebView class instead of using UIWebView."
So unfortunately it seems that their are many hoops you need to jump through to get this all going so your best bet for now is to just pester Apple and dupe Twitters radar.
You could try using a http cache, but I don't think it would work as the Safari View Controller is working as a separate process (probably the same as Safari), so that's why it e.g. circumvents ATS.
The only way I can think of this working is to somehow force the user's Safari to load it? openURL: or adding to Reading List maybe? This doesn't sound like a viable solution.
You can always experiment with custom presentation of the view controller, attach it the view hierarchy, trigger appearance events, but set its frame to CGRectMake(0,0,1,1) or attach it somewhere off-screen, then wait a while and represent it with a correct frame.
you can download the web page using the following code . and represent it with the help of svc
let data:NSData?
do {
let weatherData = try NSData(contentsOfURL: NSURL(string: remote_url)!, options: NSDataReadingOptions())
data = weatherData
print(weatherData)
} catch {
print(error)
}
and load it when you needed in the svc
While it's technically possible to use the solution above to achieve what you're asking, this may not pass App Store review. Per the SFSafariViewController docs:
In accordance with App Store Review Guidelines, this view controller must be used to visibly present information to users; the controller may not be hidden or obscured by other views or layers. Additionally, an app may not use SFSafariViewController to track users without their knowledge and consent.

How can I run 2 game objects in XNA, or change in real time the draw destination?

I'm new here!
I searched internet a lot for my question, but I didn't found anything - or I'm really thinking wrong.
I program on VB.NET since 2 years, and on XNA since 6 months. I built a game and an editor for the game, and they are running great.
The question i about my editor (for an RPG game), and I'll try to explain at my best.
I have a main form with menustrips on top and a big picturebox covering the entire form, a picbox that is binded to the Game1 object when it start with the command Run().
The Game1 object handles two classes, that are basically panels that it draws on the picbox of the main form: a tileset panel in the left down the tabpage, and a map panel on the right. This works perfectly.
The problem is when for the first time yesterday I tried to draw with XNA on a form. I have multiple forms to manage NPCs, equipment, conditions, events, variables, etc and in the event form, I have a tabpage that manages map teleport events. On this tabpage I have a list of maps and a picbox where I want to draw a small view of the selected map. For this, I created of course a minimap panel with it's own draw and update methods.
...but of course, the minimap appears on the main form on the normal map.
I tried to change in real time the DeviceWindowHandle, but I failed... apparently, it changes only during the Run()
I tried to create a new game object and binding him to the event teleport form, but in the moment of lunching the Run() of this object, the debugger stops saying that I cannot launch more that one game loop in a thread.
I can't believe that XNA doesn't let to draw multiple things on different forms... and I can not pause the main loop from the event form (which is called from the NPC form) to start the minimap loop!
I think that is something really easy that unfortunately I don't know...
I'm getting crazy and lost... what I can do?
Please help me, thanks!!
Here's an example of what I commented (Sorry it's in C# but I don't really write VB.Net. Translating it should be pretty straight forward though):
private MainGame mainGame;
private ToolboxGame toolbox1;
private ToolboxGame toolbox2;
// And this should be put in some Form Initialization method:
Task.Factory.StartNew(() => {
this.mainGame = new MainGame(imgEditorPictureBox.Handle)
this.mainGame.Run();
}
Task.Factory.StartNew(() => {
this.toolbox1 = new ToolboxGame(toolbox1PictureBox.Handle)
this.toolbox1.Run();
}
Task.Factory.StartNew(() => {
this.toolbox2 = new ToolboxGame(toolbox2PictureBox.Handle)
this.toolbox2.Run();
}
Something like that should do it. Obviously whenever you "move" variables from one "game" to another, keep in mind that they run on different threads, so anywhere you use it, you'll need to
lock (dummyObject)
{
// Use data
}
to make sure you're not accessing it in one game, while the other is trying to set it.
Locking in VB.Net: Is there a lock statement in VB.NET?
You'll obviously need to come up with some smart infrastructure to get this working smoothly, but since you've made a game and editor before, I'm sure this should not prove a humongous challenge.
All you want show to the player you need draw in the game window. You have one Game with one GraphicsDevice and by default all you draw will be rendered on the game window. But you can call GraphicsDevice.SetRenderTarget method to change render target. Call it with RenderTarget2D object as parameter and anithing you will draw after this will be rendered to that render target.
Next you need call GraphicsDevice.SetRenderTarget(null) to set game window as render target again.
There is my (uncompleted yet) custom GUI realization for XNA. I hope it can help you.
Update
class Game1 : Game
{
GraphicsDevice graphicsDevice;
SpriteBatch spriteBatch;
public RenderTarget2D MinimapRenderBuffer;
public RenderTarget2D AnotherRenderBuffer1;
public RenderTarget2D AnotherRenderBuffer2;
public EventHandler RenderBuffersUpdated;
void Initialize()
{
// Better initialize them only once, don't do it in Draw method
this.MinimapRenderBuffer = new RenderTarget2D(this.graphicsDevice, 100, 100); // any size you want
this.AnotherRenderBuffer1 = new RenderTarget2D(this.graphicsDevice, 50, 50);
this.AnotherRenderBuffer2 = new RenderTarget2D(this.graphicsDevice, 500, 500);
}
void Draw()
{
this.graphicsDevice.SetRenderTarget(this.MinimapRenderBuffer);
// draw minimap to MinimapRenderBuffer
this.graphicsDevice.SetRenderTarget(this.AnotherRenderBuffer1);
// draw whatewer to AnotherRenderBuffer1
this.graphicsDevice.SetRenderTarget(this.AnotherRenderBuffer2);
// draw whatewer to AnotherRenderBuffer2
this.graphicsDevice.SetRenderTarget(null);
// now draw to screen
if (this.RenderBuffersUpdated != null)
{
RenderBuffersUpdated(null, null);
}
}
}
And use rendertargets in your editor when event raised. And you can convert them to bitmaps.

How do I make it so that when 2 movie clips collide, I jump to another frame?

I'm a total newbie at flash.
I'm on flash CS6, and action script 2.0.
What I'm trying to do, is make it so that when a movie clip (bird_mc) collides with
another movie clip (missile_mc), then the movie jumps to a later frame.
My script below doesn't include missile_mc, and this is surely a problem, so how do
I get these two movie clips to when touch move the movie to another frame?
The bird_mc has action script to move up and down with the up and down arrow keys, and
the action script below is connected to frame 1.
Please help, I have no idea what is required to make this work, as I am a beginner!
My action script may be all completely wrong, so anything new or any edition of mine
is great.
Here is the action script on frame 1:
if (_root.bird_mc.hitTest(_x, _y, true)) {
_root.gotoAndStop(2);
}
If your movieClips and your actionScript code are all in frame 1:
this.onEnterFrame = function():Void {
if (bird_mc.hitTest(missile_mc._x, missile_mc._y, true)) {
gotoAndStop(2);
}
}
If you put your code within the movieclip missile_mc:
this.onEnterFrame = function():Void {
if (_parent.bird_mc.hitTest(_x, _y, true)) {
_parent.gotoAndStop(2);
}
}

Frame Listener in QMLOgre Lib Freeze Window

I'm newbie in using ogre3D and I need help on a certain point!
I'm trying a library mixing ogre3D engine and qtml :
http://advancingusability.wordpress.com/2013/08/14/qmlogre-is-now-a-library/
this library works fine when you want to draw some object and rotate or translate these objects already initialise in a first step.
void initialize(){
// we only want to initialize once
disconnect(this, &ExampleApp::beforeRendering, this, &ExampleApp::initializeOgre);
// start up Ogre
m_ogreEngine = new OgreEngine(this);
m_root = m_ogreEngine->startEngine();
m_ogreEngine->setupResources();
m_ogreEngine->activateOgreContext();
//draw a small cube
new DebugDrawer(m_sceneManager, 0.5f);
DrawCube(100,100,100);
DebugDrawer::getSingleton().build();
m_ogreEngine->doneOgreContext();
emit(ogreInitialized());
}
but If you want to draw or change the scene after this initialisation step it is problematic!
In fact in Ogre3D only (without the qtogre library), you have to use a frameListener
which will connect the rendering thread and allow a repaint of your scene.
But here, we have two ContextOpengl: one for qt and the other one for Ogre.
So If you try to put the common part of code :
createScene();
createFrameListener();
// La Boucle de rendu
m_root->startRendering();
//createScene();
while(true)
{
Ogre::WindowEventUtilities::messagePump();
if(pRenderWindow->isClosed())
std::cout<<"pRenderWindow close"<<std::endl;
if(!m_root->renderOneFrame())
std::cout<<"root renderOneFrame"<<std::endl;
}
the app will freeze! I know that startRendering is a render loop itself, so the loop below never gets executed.
But I don't know where to put those line or how to correct this part!
I've also try to add a background buffer and to swap them :
void OgreEngine::updateOgreContext()
{
glPopAttrib();
glPopClientAttrib();
m_qtContext->functions()->glUseProgram(0);
m_qtContext->doneCurrent();
delete m_qtContext;
m_BackgroundContext= QOpenGLContext::currentContext();
// create a new shared OpenGL context to be used exclusively by Ogre
m_BackgroundContext = new QOpenGLContext();
m_BackgroundContext->setFormat(m_quickWindow->requestedFormat());
m_BackgroundContext->setShareContext(m_qtContext);
m_BackgroundContext->create();
m_BackgroundContext->swapBuffers(m_quickWindow);
//m_ogreContext->makeCurrent(m_quickWindow);
}
but i've also the same error:
OGRE EXCEPTION(7:InternalErrorException): Cannot create GL vertex buffer in GLHardwareVertexBuffer::GLHardwareVertexBuffer at Bureau/bibliotheques/ogre_src_v1-8-1/RenderSystems/GL/src/OgreGLHardwareVertexBuffer.cpp (line 46)
I'm very stuck!
I don't know what to do?
Thanks!

Create Backgroundthread Monogame

I made a game for Windows 8 in monogame but running into a problem. We finally got our hands on a Surface RT but we noticed that the loading times are really long on this device. We looked at several other games and noticed that this wasn't an uncommon issue. To fight the boredom of the user during the loading of the resources I want to draw a loading bar and some random facts onto the screen. The problem is that loading the resources blocks the rest of the game and doesn't allow me to draw anything because it stays stuck at the initializing part.
I searched for creating a lose Thread but found that Windows Store didn't support that so now my question to you how can I load my resources in the background or another way to not block the complete game and be able to call handle my Draw() function to draw a loading bar onto my screen.
I did something like this:
protected volatile bool ContentLoaded = false;
protected async override void LoadContent()
{
base.LoadContent();
Enabled = false;
await ThreadPool.RunAsync(new WorkItemHandler(LoadAllContent));
}
protected void LoadAllContent(Windows.Foundation.IAsyncAction action)
{
if (action.Status == Windows.Foundation.AsyncStatus.Error)
System.Diagnostics.Debug.WriteLine(action.ErrorCode);
// load your contents
ContentLoaded = true;
Enable = true;
}
And at the beginning of your Draw method:
if (!ContentLoaded)
{
// draw your loading screen
return;
}
If you want a progress bar you need a counter to increase every resource you've loaded, then in your Draw your bar has to relate to that counter.