|
| 1 | +--- |
| 2 | +layout: tutorial |
| 3 | +title: Mobile |
| 4 | +--- |
| 5 | + |
| 6 | +The `engo` game engine, and Go in general, has two ways to build for a |
| 7 | +mobile device: binding or building. |
| 8 | + |
| 9 | +First, you'll need to [install Go Mobile] (https://github.com/golang/go/wiki/Mobile). |
| 10 | + |
| 11 | +## Using gomobile build |
| 12 | + |
| 13 | +To use gomobile build, simply go to the folder that you have your main package |
| 14 | +and run |
| 15 | + |
| 16 | +```go |
| 17 | +gomobile build |
| 18 | +``` |
| 19 | + |
| 20 | +A few caveats: |
| 21 | + |
| 22 | +* All your asset files must be in the asset folder to be accessed |
| 23 | +* You only have access to touch input and ogl. So you can't use things like |
| 24 | +Facebook or AdMob's sdks. This includes things like gradle and pro-guard |
| 25 | +* You'll get a generic AndroidManifest, unless you include one yourself in the |
| 26 | +same folder as your main package |
| 27 | + |
| 28 | +## Using gomobile bind |
| 29 | + |
| 30 | +Using bindings allows access to and from the Android system, and you can just |
| 31 | +build into your glue whatever functions you need to be called or to call. |
| 32 | +However, it is more complicated to initially setup. This is taken from [here] |
| 33 | +(https://github.com/Noofbiz/MouseTests) if you want to look at the code while |
| 34 | +reading this. |
| 35 | + |
| 36 | +#### Setting up your game library |
| 37 | +First thing to notice is the file game.go here. It's an (almost) exact copy of main.go, except it's a library instead of a main package. You'll need to add |
| 38 | + |
| 39 | +```go |
| 40 | +//+build !mobilebind |
| 41 | +``` |
| 42 | +to the top (before package declaration) of your main, and then |
| 43 | + |
| 44 | +```go |
| 45 | +//+build mobilebind |
| 46 | +``` |
| 47 | + |
| 48 | +to the top of the library. The other differences are that when loading a file, you can't keep assets in an asset folder for binding, so instead you'll need to use go-bindata to turn your assets into binary data. Then to load the files use |
| 49 | + |
| 50 | +```go |
| 51 | + b, err := assets.Asset("icon.png") |
| 52 | + if err != nil { |
| 53 | + log.Panic("no such icon") |
| 54 | + } |
| 55 | + engo.Files.LoadReaderData("icon.png", bytes.NewReader(b)) |
| 56 | +``` |
| 57 | +and when you're starting a game, your main becomes a start function that can be called from the androidglue package |
| 58 | + |
| 59 | +```go |
| 60 | +func Start(width, height int) { |
| 61 | + opts := engo.RunOptions{ |
| 62 | + Title: "Mouse Demo", |
| 63 | + Width: 1024, |
| 64 | + Height: 640, |
| 65 | + MobileWidth: width, |
| 66 | + MobileHeight: height, |
| 67 | + } |
| 68 | + engo.Run(opts, &DefaultScene{}) |
| 69 | +} |
| 70 | +``` |
| 71 | +The additional options, as well as the width and height being passed in, are for Android to tell your game the actual dimensions of the screen. |
| 72 | + |
| 73 | +#### Setting up the android glue |
| 74 | +Next, you'll need a package that handles communication between your Go library and Android. That's where the package androidglue comes in! androidglue.go just contains exported functions that Android can call to communicate with your game, such as tell it when to start, stop, or when touch events occur. You could potentially add more to this, for handling when to trigger a handler for the Facebook SDK or to preload an intersitial ad. |
| 75 | + |
| 76 | +```go |
| 77 | +//+build mobilebind |
| 78 | + |
| 79 | +package androidglue |
| 80 | + |
| 81 | +import ( |
| 82 | + "github.com/Noofbiz/mousetests" |
| 83 | + |
| 84 | + "engo.io/engo" |
| 85 | +) |
| 86 | + |
| 87 | +var running bool |
| 88 | + |
| 89 | +func Start(width, height int) { |
| 90 | + running = true |
| 91 | + mousetests.Start(width, height) |
| 92 | +} |
| 93 | + |
| 94 | +func Update() { |
| 95 | + engo.RunIteration() |
| 96 | +} |
| 97 | + |
| 98 | +func IsRunning() bool { |
| 99 | + return running |
| 100 | +} |
| 101 | + |
| 102 | +func Touch(x, y, action int) { |
| 103 | + engo.TouchEvent(x, y, action) |
| 104 | +} |
| 105 | + |
| 106 | +func Stop() { |
| 107 | + running = false |
| 108 | + engo.MobileStop() |
| 109 | +} |
| 110 | + |
| 111 | +``` |
| 112 | + |
| 113 | +And, that's it for the Go side of things. All that's left for Go is to use (inside the androidglue folder) |
| 114 | + |
| 115 | +``` |
| 116 | +gomobile bind -target=android -tags=mobilebind |
| 117 | +``` |
| 118 | + |
| 119 | +to compile the .aar library to import into Android Studio. |
| 120 | + |
| 121 | +#### Setting up Android side |
| 122 | + |
| 123 | +To setup the Android side, I used Android Studio. There's also a way to do it via gradle and the command line, but I don't know the exact details on doing that at the moment. |
| 124 | + |
| 125 | +Start by creating a new project in Android Studio. Phone and Tablet with minimum SDK set to 15 and with no starting activity. In the AndroidManifest.xml, add a main activity. Mine ends up looking like this |
| 126 | + |
| 127 | +```xml |
| 128 | +<manifest xmlns:android="http://schemas.android.com/apk/res/android" |
| 129 | + |
| 130 | + package="io.engo.mousetests"> |
| 131 | + |
| 132 | + <application |
| 133 | + android:allowBackup="true" |
| 134 | + android:icon="@mipmap/ic_launcher" |
| 135 | + android:label="@string/app_name" |
| 136 | + android:roundIcon="@mipmap/ic_launcher_round" |
| 137 | + android:supportsRtl="true" |
| 138 | + android:theme="@style/AppTheme"> |
| 139 | + <activity |
| 140 | + android:name=".MainActivity" |
| 141 | + android:screenOrientation="landscape"> |
| 142 | + <intent-filter> |
| 143 | + <action android:name="android.intent.action.MAIN" /> |
| 144 | + |
| 145 | + <category android:name="android.intent.category.LAUNCHER" /> |
| 146 | + </intent-filter> |
| 147 | + </activity> |
| 148 | + </application> |
| 149 | + |
| 150 | +</manifest> |
| 151 | +``` |
| 152 | + |
| 153 | +and then to styles.xml I added the following to give the full screen to the gl surface view |
| 154 | + |
| 155 | +```xml |
| 156 | + <item name="windowNoTitle">true</item> |
| 157 | + <item name="android:windowFullscreen">true</item> |
| 158 | + <item name="android:windowContentOverlay">@null</item> |
| 159 | +``` |
| 160 | + |
| 161 | +Next we have to add a main activity, by creating a MainActivity.java file. |
| 162 | + |
| 163 | +```java |
| 164 | +package io.engo.mousetests; |
| 165 | + |
| 166 | +import android.opengl.GLSurfaceView; |
| 167 | +import android.support.v7.app.AppCompatActivity; |
| 168 | +import android.os.Bundle; |
| 169 | +import android.util.Log; |
| 170 | + |
| 171 | +public class MainActivity extends AppCompatActivity { |
| 172 | + |
| 173 | + @Override |
| 174 | + protected void onCreate(Bundle savedInstanceState) { |
| 175 | + super.onCreate(savedInstanceState); |
| 176 | + setContentView(R.layout.activity_main); |
| 177 | + } |
| 178 | + |
| 179 | + private GLSurfaceView glSurfaceView() { |
| 180 | + return (GLSurfaceView)this.findViewById(R.id.glview); |
| 181 | + } |
| 182 | + |
| 183 | + @Override |
| 184 | + protected void onPause() { |
| 185 | + super.onPause(); |
| 186 | + this.glSurfaceView().onPause(); |
| 187 | + } |
| 188 | + |
| 189 | + @Override |
| 190 | + protected void onResume() { |
| 191 | + super.onResume(); |
| 192 | + this.glSurfaceView().onResume(); |
| 193 | + } |
| 194 | +} |
| 195 | +``` |
| 196 | + |
| 197 | +as well as a style xml for it. Here's activity_main.xml in the layout folder |
| 198 | + |
| 199 | +```xml |
| 200 | +<?xml version="1.0" encoding="utf-8"?> |
| 201 | +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" |
| 202 | + xmlns:tools="http://schemas.android.com/tools" |
| 203 | + android:layout_width="match_parent" |
| 204 | + android:layout_height="match_parent" |
| 205 | + tools:context="io.engo.mousetests.MainActivity" |
| 206 | + android:keepScreenOn="true" > |
| 207 | + <io.engo.mousetests.EngoGLSurfaceView |
| 208 | + android:id="@+id/glview" |
| 209 | + android:layout_width="wrap_content" |
| 210 | + android:layout_height="wrap_content" |
| 211 | + android:layout_centerHorizontal="true" |
| 212 | + android:layout_centerVertical="true" /> |
| 213 | +</RelativeLayout> |
| 214 | +``` |
| 215 | + |
| 216 | +and finally, create the glSurfaceView. EngoGLSurfaceView.java |
| 217 | + |
| 218 | +```java |
| 219 | +package io.engo.mousetests; |
| 220 | + |
| 221 | +import android.content.Context; |
| 222 | +import android.content.res.Resources; |
| 223 | +import android.opengl.GLSurfaceView; |
| 224 | +import android.util.Log; |
| 225 | +import android.util.AttributeSet; |
| 226 | +import android.view.MotionEvent; |
| 227 | + |
| 228 | +import javax.microedition.khronos.egl.EGLConfig; |
| 229 | +import javax.microedition.khronos.opengles.GL10; |
| 230 | + |
| 231 | +import androidglue.*; |
| 232 | + |
| 233 | +public class EngoGLSurfaceView extends GLSurfaceView { |
| 234 | + |
| 235 | + private class EngoRenderer implements Renderer { |
| 236 | + |
| 237 | + private boolean mErrored; |
| 238 | + |
| 239 | + @Override |
| 240 | + public void onDrawFrame(GL10 gl) { |
| 241 | + if (mErrored) { |
| 242 | + return; |
| 243 | + } |
| 244 | + try { |
| 245 | + Androidglue.update(); |
| 246 | + } catch (Exception e) { |
| 247 | + Log.e("Go Error", e.toString()); |
| 248 | + mErrored = true; |
| 249 | + } |
| 250 | + } |
| 251 | + |
| 252 | + @Override |
| 253 | + public void onSurfaceCreated(GL10 gl, EGLConfig config) { |
| 254 | + } |
| 255 | + |
| 256 | + @Override |
| 257 | + public void onSurfaceChanged(GL10 gl, int width, int height) { |
| 258 | + } |
| 259 | + } |
| 260 | + |
| 261 | + public EngoGLSurfaceView(Context context) { |
| 262 | + super(context); |
| 263 | + initialize(); |
| 264 | + } |
| 265 | + |
| 266 | + public EngoGLSurfaceView(Context context, AttributeSet attrs) { |
| 267 | + super(context, attrs); |
| 268 | + initialize(); |
| 269 | + } |
| 270 | + |
| 271 | + private void initialize() { |
| 272 | + setEGLContextClientVersion(2); |
| 273 | + setEGLConfigChooser(8, 8, 8, 8, 0, 0); |
| 274 | + setRenderer(new EngoRenderer()); |
| 275 | + } |
| 276 | + |
| 277 | + @Override |
| 278 | + public void onLayout(boolean changed, int left, int top, int right, int bottom) { |
| 279 | + super.onLayout(changed, left, top, right, bottom); |
| 280 | + |
| 281 | + try { |
| 282 | + if (!Androidglue.isRunning()) { |
| 283 | + Androidglue.start(Resources.getSystem().getDisplayMetrics().widthPixels, Resources.getSystem().getDisplayMetrics().heightPixels); |
| 284 | + } |
| 285 | + } catch (Exception e) { |
| 286 | + Log.e("Go Error", e.toString()); |
| 287 | + } |
| 288 | + } |
| 289 | + |
| 290 | + @Override |
| 291 | + public boolean onTouchEvent(MotionEvent e) { |
| 292 | + for (int i = 0; i < e.getPointerCount(); i++) { |
| 293 | + Androidglue.touch((int)e.getX(i), (int)e.getY(i), e.getActionMasked()); |
| 294 | + } |
| 295 | + return true; |
| 296 | + } |
| 297 | + |
| 298 | + @Override |
| 299 | + public void onPause() { |
| 300 | + try { |
| 301 | + if (Androidglue.isRunning()) { |
| 302 | + Androidglue.stop(); |
| 303 | + } |
| 304 | + } catch (Exception e) { |
| 305 | + Log.e("Go Error", e.toString()); |
| 306 | + } |
| 307 | + } |
| 308 | +} |
| 309 | +``` |
| 310 | + |
| 311 | +Now the last part is to import the aar created by gomobile bind |
| 312 | + |
| 313 | +File -> New -> New Module... |
| 314 | + |
| 315 | +Pick Import .JAR/.AAR Package |
| 316 | + |
| 317 | +Navigate to the .AAR package you created and import it |
| 318 | + |
| 319 | +Then add it as a dependency to your android project |
| 320 | + |
| 321 | +File -> Project Structure -> app -> on the dependencies tab |
| 322 | + |
| 323 | +Then click the +, and under module dependencies you'll find your imported androidglue library. Everything should build nicely and you can then run it in an emulator or a device! |
0 commit comments