[iOS] Multiple issues (and solutions/workarounds) trying to run jme3 app

It’s been plenty of hours spent on this during the weekend but I finally found out! iOS has some bad gl implementation and default framebuffer is not necessarily 0 but any random number (most times 1). Some references on this:

So I modified GLRenderer to take this into consideration as follows:

private defaultFBO=0;

public void initialize() {
    /* current code not shown to reduce post size */
    IntBuffer tmp=BufferUtils.createIntBuffer(16);
    gl.glGetInteger(36006, tmp); // 36006 stands for GL_FRAMEBUFFER_BINDING
    tmp.rewind();
    int fbOnLoad=tmp.get();
    if(fbOnLoad>0)
    {
        System.out.println("Overriding default FB to: " + fbOnLoad);
        this.defaultFBO=fbOnLoad;
    }
}

private void bindFrameBuffer(FrameBuffer fb) {
    if (fb == null) {
        if (context.boundFBO != defaultFBO) {
            glfbo.glBindFramebufferEXT(GLFbo.GL_FRAMEBUFFER_EXT, defaultFBO);
            statistics.onFrameBufferUse(null, true);
            context.boundFBO = defaultFBO;
            context.boundFB = null;
        }
    } else {
        assert fb.getId() != -1 && fb.getId() != 0;
        if (context.boundFBO != fb.getId()) {
            glfbo.glBindFramebufferEXT(GLFbo.GL_FRAMEBUFFER_EXT, fb.getId());
            context.boundFBO = fb.getId();
            context.boundFB = fb;
            statistics.onFrameBufferUse(fb, true);
        } else {
            statistics.onFrameBufferUse(fb, false);
        }
    }
}

This way the proper framebuffer is used and the scene with filters is properly rendered now. :v:

About non power of two textures, jme adds Caps.PartialNonPowerOfTwoTextures when rendering using GLES2.0 but I don’t know what this capability implies… Anyway, I use power of two textures 99.9% percent of the times just in case

I know apple deprecated openGL in favour of metal but I don’t think they’ll remove it anytime soon from their OSs. Or at least I hope so. In fact, as my app relies on plenty of jme3 functionality requiring gles3 I’m implementing missing gles3 stuff for iOS (currently working 90%) and I’m planning on creating a PR for it during this week, including previous fix. I know this may be a loss of time, but while gl is not removed it’s a good improvement to have…

Are you having a look into implementing a metal renderer for jme? Wow! this can be a really hard topic to address.

I’m fine with using xcode 11, I only own a hackintosh running 10.15 and I don’t have any intention to update :stuck_out_tongue:

I didn’t know openAL is also deprecated… For the long term ios will require a lot of work in jme to support this platform :frowning:

I knew about avian also. As I posted before maybe it should be replaced with a newer and supported jvm. I’m not an expert in jdk inners neither in apple and/or ios itself, but I have some spare time now and I’d be glad to help. Have you thought on any alternatives? Do you have a hub thread for this? We could talk about it there.

I confirm iOS sound is currently working, just tested it

3 Likes

Thanks for confirmation :slightly_smiling_face:

By the way, for Avian replacement, you may want to take a look at Oracle’s Graalvm native image.

And for GLES → Metal, you may want to look at

iOS support is planned yet.

Hope you find it useful.

Thanks for the comment.

I was having a look at graalvm and also to minijvm (GitHub - digitalgust/miniJVM: Develop iOS Android app in java, Cross platform java virtual machine , the minimal jvm .) but not in the mood to get into this still.

About google’s angle, sadly it still doesn’t support metal, so it’s not an option for ios nowadays but could be a good replacement as jme3 default renderer instead of having to mess ourselves with vulkan, gl and metal

Other issue here… If running a really simple app (skybox + a plain blue cube) if using iphone 11pro (ios 13.0) it’s not rendering fullscreen.

Regular rendering tested on physical iphone 5 12.4, iphone 8plus (13.3) and some other:

Bad rendering at 11pro:

Any ideas?

EDIT:

ios 13.0 has this weird behaviour, using any iphone in the simulator running 13.0 breaks rendering while 12.4 and 13.3 work as expected.

Also, on ios13.0 rotating the device fixes it as described in this post

that is to do with the scaling which is explained how to resolve in both mine and Dark Chaos’s previous posts. i’ll find it or repost the code sample shortly

So in short the screen on iOS is rendered at 1024x768 and if your device is higher res than that (which they all are) then it will show in the corner.

You have to scale it but also take into account the orientation (landscape/portrait) and the relative resolution of the device.

I’ll just give you my code to achieve this. It’s in the jmeAppDelegate and you put it in the rotate notification event.

#pragma mark - Device Actions

-(void)rotate {
[self didRotate:nil];
}

  • (void)didRotate:(NSNotification *)notification
    {

    if (self.vm != nil) {
    CGRect originalFrame = [[UIScreen mainScreen] bounds];
    CGRect frame = [_glview convertRect:originalFrame fromView:nil];

      UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
      UIDeviceOrientation dorientation = [[UIDevice currentDevice] orientation];
      
      JNIEnv* e = getEnv(self.vm);
      if (e) {
          float scale = _glview.contentScaleFactor;
          int width = (int)frame.size.width;
          int height = (int)frame.size.height;
          
          if ((dorientation == UIDeviceOrientationLandscapeRight || dorientation == UIDeviceOrientationLandscapeLeft) && (orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown)) {
              
              
              (*e)->CallVoidMethod(e, self.harness, self.reshapeMethod, (int)(frame.size.height * scale), (int)(frame.size.width * scale), (int)frame.size.height, (int)frame.size.width);
              
          }
          else
          {
              (*e)->CallVoidMethod(e, self.harness, self.reshapeMethod, (int)(frame.size.width * scale), (int)(frame.size.height * scale), (int)frame.size.width, (int)frame.size.height);
              
          }
                   
          
          if ((*e)->ExceptionCheck(e)) {
              NSLog(@"Could not invoke iOS Harness reshape");
              (*e)->ExceptionDescribe(e);
              (*e)->ExceptionClear(e);
          }
      }
    

    }

}

This is my jmeHarness which includes another fix for the touch location also needs to be scaled to fit the device resolution

import com.inventiveinspiration.ChatterGames.CCHarness;
import com.inventiveinspiration.ChatterGames.Main;
import com.jme3.system.ios.IosHarness;
import com.jme3.input.ios.IosInputHandler;
import com.jme3.renderer.opengl.GLRenderer;
import com.jme3.system.JmeContext;
import com.jme3.system.AppSettings;
import java.util.logging.Level;
import java.util.logging.Logger;

public class JmeAppHarness extends IosHarness implements CCHarness {

private float IOSScaleFactorX = 1.0f;
private float IOSScaleFactorY = 1.0f;
private int originalWidth = 1024;
private int originalHeight = 768;

private static final Logger logger = Logger.getLogger(JmeAppHarness.class.getName());
protected GLRenderer renderer;
protected IosInputHandler input;
protected boolean autoFlush = true;



public JmeAppHarness(long id) {
    super(id);
    app = new com.inventiveinspiration.ChatterGames.Main();

   
    

    AppSettings settings = new AppSettings(true);
   
    app.setSettings(settings);
   
    originalWidth = 1024;
    originalHeight = 768;


    app.start();

    logger.log(Level.FINE, "JmeAppHarness constructor");          
    app.gainFocus();

       
}

@Override
public void appPaused() {
    logger.log(Level.FINE, "JmeAppHarness appPaused");
}

@Override
public void appReactivated() {
}

@Override
public void appClosed() {
    logger.log(Level.FINE, "JmeAppHarness appClosed");
    app.stop();
}

@Override
public void appUpdate() {
    logger.log(Level.FINE, "JmeAppHarness appUpdate");
    //app.update();
}

@Override
public void appDraw() {
    logger.log(Level.FINE, "JmeAppHarness appDraw");
    if (renderer == null) {
        JmeContext iosContext = app.getContext();
        renderer = (GLRenderer) iosContext.getRenderer();
        renderer.initialize();
        input = (IosInputHandler) iosContext.getTouchInput();
        input.initialize();
    } else {
        app.update();
        if (autoFlush) {
            renderer.postFrame();
        }
    }
}

@Override
public void appReshape(int width, int height) {
    logger.log(Level.FINE, "JmeAppHarness reshape");
    AppSettings settings = app.getContext().getSettings();
    settings.setResolution(width, height);
    if (renderer != null) {
        app.reshape(width, height);
    }
    if (input != null) {
        input.loadSettings(settings);
    }
}

public void injectTouchBegin(int pointerId, long time, float x, float y) {
    if (input != null) {
        logger.log(Level.FINE, "JmeAppHarness injectTouchBegin");
        input.injectTouchDown(pointerId, time, x, y);
    }
}

public void injectTouchMove(int pointerId, long time, float x, float y) {
    if (input != null) {
        logger.log(Level.FINE, "JmeAppHarness injectTouchMove");
        input.injectTouchMove(pointerId, time, x, y);
    }
}

public void injectTouchEnd(int pointerId, long time, float x, float y) {
    if (input != null) {
        logger.log(Level.FINE, "JmeAppHarness injectTouchEnd");
        input.injectTouchUp(pointerId, time, x, y);
    }
}


@Override
public void reshapeGame(int width, int height, int origwidth, int origheight) {
    logger.log(Level.FINE, "JmeAppHarness reshape");
    AppSettings settings = app.getContext().getSettings();

    if (width < 1) {
        width = 640;
    }
    if (height < 1) {
        height = 480;
    }

    if (origwidth < 1) {
        origwidth = 640;
    }
    if (origheight < 1) {
        origheight = 480;
    }        
    

    originalWidth = origwidth;
    originalHeight = origheight;

    settings.setResolution(width, height);
    IOSScaleFactorX = (float) width / (float) originalWidth;
    IOSScaleFactorY = (float) height / (float) originalHeight;

    ((Main) app).setIOSScaleFactor(IOSScaleFactorX, IOSScaleFactorY, width, height, origwidth, origheight);

    if (renderer != null) {

        app.reshape(width, height);
    }

    if (input != null) {

        input.loadSettings(settings);

        ((Main) app).IOSInitialiseTouch(origwidth, origheight);

    }

}




public void IOSInitialiseTouch() {

    if (renderer != null) {

        ((Main) app).IOSInitialiseTouch(originalWidth, originalHeight);
    } else {
        System.out.println("Can't reset touch because Renderer is not ready....");
    }
};

}

and I have a method in my Main class which actions the resolution and touch scaling when the device is rotated.

You also have to call rotate once when the app starts or you have to rotate the device to get it scale properly.

public void IOSInitialiseTouch(int W, int H) {

    settings = getContext().getSettings();
    settings.setHeight(H);
    settings.setWidth(W);
    getContext().setSettings(settings);
    settings = getContext().getSettings();
    resetWidth = W;
    resetHeight = H;
    
}

And finally ive added a slightly amended touch events which includes the timestamp to be cast to a long as it fails otherwise. That may have been fixed since I wrote this code.

#pragma mark - GLKViewDelegate

- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
    JNIEnv* e = getEnv(self.vm);
    if (e) {
        (*e)->CallVoidMethod(e, self.harness, self.drawMethod);
        if ((*e)->ExceptionCheck(e)) {
            NSLog(@"Could not invoke iOS Harness update");
            (*e)->ExceptionDescribe(e);
            (*e)->ExceptionClear(e);
        }
    }
}

#pragma mark - GLKViewControllerDelegate

- (void)glkViewControllerUpdate:(GLKViewController *)controller {
    JNIEnv* e = getEnv(self.vm);
    if (e) {
        (*e)->CallVoidMethod(e, self.harness, self.updateMethod);
        if ((*e)->ExceptionCheck(e)) {
            NSLog(@"Could not invoke iOS Harness update");
            (*e)->ExceptionDescribe(e);
            (*e)->ExceptionClear(e);
        }
    }
    
}


#pragma mark - UIResponder

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    NSLog(@"touchesBegan");
    if (self.vm == nil) {
        
    }
    else
    {
        JNIEnv* e = getEnv(self.vm);
        if (e) {
            UITouch *touch = [touches anyObject];
            CGPoint position = [touch locationInView: nil];
            (*e)->CallVoidMethod(e, self.harness, self.injectTouchBegin, 0, (jlong)touch.timestamp, position.x, position.y);
            if ((*e)->ExceptionCheck(e)) {
                NSLog(@"Could not invoke iOS Harness injectTouchBegin");
                (*e)->ExceptionDescribe(e);
                (*e)->ExceptionClear(e);
            }
        }
    }
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    NSLog(@"touchesMoved");
    if (self.vm == nil) {
        
    }
    else
    {
        JNIEnv* e = getEnv(self.vm);
        if (e) {
            UITouch *touch = [touches anyObject];
            CGPoint position = [touch locationInView: nil];
            (*e)->CallVoidMethod(e, self.harness, self.injectTouchMove, 0, (jlong)touch.timestamp, position.x, position.y);
            if ((*e)->ExceptionCheck(e)) {
                NSLog(@"Could not invoke iOS Harness injectTouchMove");
                (*e)->ExceptionDescribe(e);
                (*e)->ExceptionClear(e);
            }
        }
    }
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    NSLog(@"touchesEnded");
    if (self.vm == nil) {
        
    }
    else
    {
        JNIEnv* e = getEnv(self.vm);
        if (e) {
            
            
            UITouch *touch = [touches anyObject];
            CGPoint position = [touch locationInView: nil];
            (*e)->CallVoidMethod(e, self.harness, self.injectTouchEnd, 0, (jlong)touch.timestamp, position.x, position.y);
            if ((*e)->ExceptionCheck(e)) {
                NSLog(@"Could not invoke iOS Harness injectTouchEnd");
                (*e)->ExceptionDescribe(e);
                (*e)->ExceptionClear(e);
            }
        }
    }
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
    NSLog(@"touchesCancelled");
}

For your info these are my interface descriptors from jmeAppDelegate

jclass harnessClass = (*e)->FindClass(e, "JmeAppHarness");
if (! (*e)->ExceptionCheck(e)) {
    jmethodID harnessConstructor = (*e)->GetMethodID(e, harnessClass, "<init>", "(J)V");
    if (! (*e)->ExceptionCheck(e)) {
        jobject harnessObject = (*e)->NewObject(e, harnessClass, harnessConstructor, (jlong)self);
        if (! (*e)->ExceptionCheck(e)) {
            self.harness = harnessObject;
            (*e)->NewGlobalRef(e, harnessObject);
            self.pauseMethod = (*e)->GetMethodID(e, harnessClass, "appPaused", "()V");
            (*e)->ExceptionCheck(e);
            self.reactivateMethod = (*e)->GetMethodID(e, harnessClass, "appReactivated", "()V");
            (*e)->ExceptionCheck(e);
           
            self.closeMethod = (*e)->GetMethodID(e, harnessClass, "appClosed", "()V");
            (*e)->ExceptionCheck(e);
            self.updateMethod = (*e)->GetMethodID(e, harnessClass, "appUpdate", "()V");
            (*e)->ExceptionCheck(e);
            self.drawMethod = (*e)->GetMethodID(e, harnessClass, "appDraw", "()V");
            (*e)->ExceptionCheck(e);
            self.reshapeMethod = (*e)->GetMethodID(e, harnessClass, "reshapeGame", "(IIII)V");
            (*e)->ExceptionCheck(e);
            self.injectTouchBegin = (*e)->GetMethodID(e, harnessClass, "injectTouchBegin", "(IJFF)V");
            (*e)->ExceptionCheck(e);
            self.injectTouchMove = (*e)->GetMethodID(e, harnessClass, "injectTouchMove", "(IJFF)V");
            (*e)->ExceptionCheck(e);
            self.injectTouchEnd = (*e)->GetMethodID(e, harnessClass, "injectTouchEnd", "(IJFF)V");
            (*e)->ExceptionCheck(e);
            self.IOSInitialiseTouch = (*e)->GetMethodID(e, harnessClass, "IOSInitialiseTouch", "()V");
            (*e)->ExceptionCheck(e);                
        }else{
            NSLog(@"Could not create new iOS Harness object");
            (*e)->ExceptionDescribe(e);
            (*e)->ExceptionClear(e);
            return NO;
        }
    }else{
        NSLog(@"Could not find iOS Harness constructor");
        (*e)->ExceptionDescribe(e);
        (*e)->ExceptionClear(e);
        return NO;
    }
}else{
    (*e)->ExceptionDescribe(e);
    (*e)->ExceptionClear(e);
    NSLog(@"Could not find iOS Harness class");
    return NO;
}
jclass harnessClass = (*e)->FindClass(e, "JmeAppHarness");
if (! (*e)->ExceptionCheck(e)) {
    jmethodID harnessConstructor = (*e)->GetMethodID(e, harnessClass, "<init>", "(J)V");
    if (! (*e)->ExceptionCheck(e)) {
        jobject harnessObject = (*e)->NewObject(e, harnessClass, harnessConstructor, (jlong)self);
        if (! (*e)->ExceptionCheck(e)) {
            self.harness = harnessObject;
            (*e)->NewGlobalRef(e, harnessObject);
            self.pauseMethod = (*e)->GetMethodID(e, harnessClass, "appPaused", "()V");
            (*e)->ExceptionCheck(e);
            self.reactivateMethod = (*e)->GetMethodID(e, harnessClass, "appReactivated", "()V");
            (*e)->ExceptionCheck(e);
           
            self.closeMethod = (*e)->GetMethodID(e, harnessClass, "appClosed", "()V");
            (*e)->ExceptionCheck(e);
            self.updateMethod = (*e)->GetMethodID(e, harnessClass, "appUpdate", "()V");
            (*e)->ExceptionCheck(e);
            self.drawMethod = (*e)->GetMethodID(e, harnessClass, "appDraw", "()V");
            (*e)->ExceptionCheck(e);
            self.reshapeMethod = (*e)->GetMethodID(e, harnessClass, "reshapeGame", "(IIII)V");
            (*e)->ExceptionCheck(e);
            self.injectTouchBegin = (*e)->GetMethodID(e, harnessClass, "injectTouchBegin", "(IJFF)V");
            (*e)->ExceptionCheck(e);
            self.injectTouchMove = (*e)->GetMethodID(e, harnessClass, "injectTouchMove", "(IJFF)V");
            (*e)->ExceptionCheck(e);
            self.injectTouchEnd = (*e)->GetMethodID(e, harnessClass, "injectTouchEnd", "(IJFF)V");
            (*e)->ExceptionCheck(e);
            self.IOSInitialiseTouch = (*e)->GetMethodID(e, harnessClass, "IOSInitialiseTouch", "()V");
            (*e)->ExceptionCheck(e);                
        }else{
            NSLog(@"Could not create new iOS Harness object");
            (*e)->ExceptionDescribe(e);
            (*e)->ExceptionClear(e);
            return NO;
        }
    }else{
        NSLog(@"Could not find iOS Harness constructor");
        (*e)->ExceptionDescribe(e);
        (*e)->ExceptionClear(e);
        return NO;
    }
}else{
    (*e)->ExceptionDescribe(e);
    (*e)->ExceptionClear(e);
    NSLog(@"Could not find iOS Harness class");
    return NO;
}
1 Like

Thanks for the code and the detailed explanation @GTWhite!!! I’ll work on this tomorrow.

Honestly, I thought most of these fixes were already included in jme as they were talked about in the forum… Are you planning on fixing it in the engine? If not, do you mind if I add them in a PR I’m preparing with other ios stuff?

1 Like

not at all, go for it.

If you get stuck let me know because it was bit rushed. I’ll try to get time to tidy it a bit.

1 Like

Hi there!

On some devices the view ratio was bad and also the resolution didn’t match the screen:

Resolution was 960x1704 (scale x3). I’ve been searching about this and I’ve found that for some projects, setting a storyboard as launch screen solved the issue so I created a dummy storyboard and assigned it to launch screen project property keeping MainWindow_iPhone.xib as main interface of the project. Now it renders correctly using native resolution 1025x2436 (scale x3):

Everything I’ve tested so far worked as expected. Does any ios developer around think this configuration could be an issue?

I’ve been testing the this deeper and current jme3 master works fine both for rendering and for touch input. There were two issues, the one detailed (and solved) in my previous post and this one.

So I tried adding current didRotate code in the applicationDidBecomeActive to solve the second one but although it’s run on start, the result on ios 13.0 is exactly the same. I think it’s an ios 13.0 issue because it fails same way in all 13.0 devices I’ve tested and doesn’t fail in any other release, at least in the simulator…

I even tried niftyGUI which is working after a quick fix I’m detailing just in case anyone gets to the same situation. When trying to build the app using nifty from jme-sdk you can get the following error:

Creating iOS resources object file for arm
Deleting: /jme-projects-3.3.2/iosTestGame/build/ios-arm/libs/bootimage-bin.o
Deleting: /jme-projects-3.3.2/iosTestGame/build/ios-arm/libs/codeimage-bin.o
Creating iOS bootimage and codeimage for arm
java/lang/NoClassDefFoundError: java/awt/FontFormatException

To solve this it’s enough to create your own class in your project as follows:

package java.awt;

public class FontFormatException extends Exception{
}

It’s safe to do so because the awt classes are loaded and used dynamicaly if they are located in the jvm

1 Like

You can’t call DidRotate explicitly (which I realise is what I said in the last post). It must be called as a result of “UIDeviceOrientationDidChangeNotification” event which is generated from the device.

When that fires, it triggers the observer that we create which in turn runs our DidRotate method.

The code for the observer is:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didRotate:) name:UIDeviceOrientationDidChangeNotification object:nil];

that goes just before the RETURN YES at the bottom of

didFinishLaunchingWithOptions

Now when you start the app if it renders small again, rotate the device and see if that corrects the scaling. If it does then let me know and I’ll dig out how I got that work automatically.

If it doesn’t work, then it could be iOS 13 but I doubt it.

Let me know.

Probably I didn’t explain myself properly, I wasn’t calling didRotate directly but copying the current working code of that method into applicationDidBecomeActive because I assumed didRotate was not run on app start.

Currently the observer is defined and the didRotate function:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Plenty of java initalization code and other stuff I'm not copying here

    //return (*e)->ExceptionCheck(e) ? NO : YES;
    return YES;
}

- (void)didRotate:(NSNotification *)notification
{
    CGRect originalFrame = [[UIScreen mainScreen] bounds];
    CGRect frame = [self.glview convertRect:originalFrame fromView:nil];
    JNIEnv* e = getEnv(self.vm);
    if (e) {
        float scale = _glview.contentScaleFactor;
        (*e)->CallVoidMethod(e, self.harness, self.reshapeMethod, (int)(frame.size.width * scale), (int)(frame.size.height * scale));
        if ((*e)->ExceptionCheck(e)) {
            NSLog(@"Could not invoke iOS Harness reshape");
            (*e)->ExceptionDescribe(e);
            (*e)->ExceptionClear(e);
        }
    }
}

In fact, it’s working properly if you rotate the device even for 13.0. The only issue is initialization when running on this ios release

EDIT:

Finally I found out, sometimes when JmeAppHarness.appReshape is called the renderer has not still been created, so resolution is not updated:

@Override
public void appReshape(int width, int height) {
    logger.log(Level.FINE, "JmeAppHarness reshape");
    AppSettings settings = app.getContext().getSettings();
    settings.setResolution(width, height);
    if (renderer != null) {
		app.reshape(width, height);
	}
	if (input != null) {
		input.loadSettings(settings);
	}
}

I’ve just changed it as follows:

@Override
public void appReshape(int width, int height) {
    logger.log(Level.FINE, "JmeAppHarness reshape");
    AppSettings settings = app.getContext().getSettings();
    settings.setResolution(width, height);
    if (renderer == null) {
        renderer = (GLRenderer)app.getContext().getRenderer();
    }
    app.reshape(width, height);
    if (input == null) {
        input = (IosInputHandler)app.getContext().getTouchInput();
    }
    input.loadSettings(settings);
}

And now the app starts correctly in all ios13.0 I’ve tested (3 different simulator devices):

I’m just missing one last issue, the one posted here:

**dyld: Symbol not found: _objc_alloc_init**

This is happening on any ios <=12.0 @GTWhite are you running your apps over anything this old? And, being that the case, how did you solve it? Could it be related to the xcode version I’m using or a badly configured project (I’ve set iphone to 9.0)? Thanks

1 Like

Yeah, ive got an old iPad 3 running on iOS 9.3 and it works ok.

Which devices and architectures are you building for?

I did have some issues with trying to build for physical and simulator in the same build.

You don’t need it though, u only need x86_64 for the simulator and when you deploy to app store then u only need to build for ARM 32 and 64 bit.

Try filtering the build for one or the other.

1 Like

I left it by default, so all archs (arm, arm64 and x86_64). Sadly I don’t own anything apart from the iphone 5s which is running 12.4. I’ll search for a way to downgrade it to be able to test it although I don’t think supporting lower ios releases is a must because devices as old as iphone 5s (from 2013) and iPad air (also from 2013) run 12.x

Hi, ive had a look at it again and there are still a few problems in the standard “basic game” for iOS.

I’ve almost finished re-creating it on 3.2.4 because 3.3 has some missing code in the compiled version of the dist. I’ll share it on Git Hub when it’s finished (probably in the morning).

I don’t know if you got it running or not, but it seems that the touch is only working in one direction.

You have to cast the timestamp to a jlong to get it to work. You will see once I’ve done it.

 UITouch *touch = [touches anyObject];
            CGPoint position = [touch locationInView: nil];
            (*e)->CallVoidMethod(e, self.harness, self.injectTouchEnd, 0,    (jlong)touch.timestamp, position.x, position.y);
            if ((*e)->ExceptionCheck(e)) {
                NSLog(@"Could not invoke iOS Harness injectTouchEnd");
                (*e)->ExceptionDescribe(e);
                (*e)->ExceptionClear(e);

}

After I get that working then I’ll look at using the 3.3 binaries.

Hi @GTWhite I got everything working, in fact I published 2 PRs related to iOS linked in this new hub post. It would be great if you, being an iOS developer, could check it and give me some feedback if you find anything not working as expected :wink:

About the touch.timestamp being casted to jlong, it was already added. Have a look at this

I see, I’m obviously looking at an old one. I’ll check it our and let you know. Great work. Well done.

On NIFTY you said you had some success with that. I could get it to compile but could never get the font to work.

Did you?

1 Like

Yes, nifty was working for me correctly, at least the basics. I tried creating a simple box with text inside and an interact to check touch coordinates. It was rendered properly and the interaction worked as expected

Maybe nifty text failing was caused by any of gles related issues that were solved for android that should also fix ios stuff, specially now that gles3 is supported.

If you have the time to test latest jme3 master and the related fix for ios template at my sdk fork it would be great! Thanks :wink:

EDIT: Both PRs have been merged, so you can use jmonkeyengine masters. If you find any issue with my changes either comment here or in the contribution thread and I’ll have a look at it

2 Likes

Both PRs have been merged, so you can use jmonkeyengine masters.

The Engine fix is included in v3.4.0-alpha3.