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;
}