Going Native Ana Redmond Founder CTO anaredmond Copenhagen
- Slides: 46
Going Native Ana Redmond Founder & CTO, @anaredmond Copenhagen Denmark
The problem: Drills and Rote learning
The solution Teach concepts Using Manipulatives in Touch-based Games 1. 5 million downloads of early prototypes (2011 -2015)
Touch-screen Manipulatives teach concepts 120 Math lessons for K-2 with 60+ Games Integrated Audio. Visual Feedback in every game Pilot Research Result: 1 Year Learning Gain (Cohen’s d: 1. 24, p<. 001) in just 3 Months
infinut Math for Android - Published K, 1 st and 2 nd grade in 2016 -2017 - 6 years to get to 60+ games /w feedback - https: //play. google. com/store/apps/dev? id=5969572456 736192268
In Theory (Dec 2017 NSF Grant Application)
In Theory (Dec 2017 NSF Grant Application)
infinut Math for i. OS too! - All 3 grades published in Dec 2018 - 6 months to port 87, 436 lines of code - https: //apps. apple. com/developer/id 1436758341
Demo App Screens Profile, Tracking, Assignments are not in Kotlin-Native Games E. g. Clothes Line, Place Value Blocks, Fractions etc. are all in Kotlin. Native
Terminology Native Code: Written in Swift on i. OS and Kotlin/Java in Android Kotlin Native Code: Written in Kotlin and runs natively on i. OS and Android. Could be platform-specific implementation if it invokes native libraries. Common or Shared Code: Shared Kotlin Native Code that runs on both platforms natively. Written once and compiled to both.
Why Kotlin-Native for infinut Math - Large number of 2 D native games Share game code and feedback logic Code has few external dependencies Code has few screens with native widgets
Architecture
Android to i. OS 2 D API Mapping
Two ways of sharing code using Kotlin. Native 1. Expects and Actual to invoke low-level platform libraries 2. Interface defined in Kotlin with platformspecific native implementation
Expects & Actual Implemention
e. g. Shapes Drawn using Bezier Paths
Common Game SDK (expects) expect class Path. Display { constructor() fun start. At(x: Double, y: Double) fun quad. To(cx: Double, cy: Double, x: Double, y: Double) fun cubic. To(c 1 x: Double, c 1 y: Double, c 2 x: Double, c 2 y: Double, x: Double, y: Double) fun set. Display(fill. Color: Int, line. Width: Double=0. 0, line. Color: Int=0) fun draw(canvas: Any? ) }
Invoke from Multi-platform Game Code to Draw a Heart Shape fun heart. Path. Display(width: Double, height: Double) : Path. Display { val p = Path. Display() p. start. At(0. 0, height / 4) p. cubic. To(0. 0, -height/12, width/2, height/4) // (1) p. cubic. To(width/2, -height/12, width, height/4) // (2) p. quad. To(width, height * 2. 5 / 4, width / 2, height) // (3) p. quad. To(0. 0, height * 2. 5 / 4, 0. 0, height / 4) // (4) return p }
Android Implementation of Path. Display import android. graphics. * actual class Path. Display { val path = Path() val paint = Paint(Paint. ANTI_ALIAS_FLAG or Paint. DITHER_FLAG) var line. Width: Double = 0. 0 var line. Color: Int = 0 var fill. Color: Int = 0 i. OS Implementation of Path. Display import kotlinx. cinterop. * import platform. UIKit. * actual class Path. Display { internal var path: UIBezier. Path = UIBezier. Path() internal var line. Width: Double = 0. 0 internal var line. Color: UIColor? = null internal var fill. Color: UIColor? = null
Android Implementation of Path. Display functions i. OS Implementation of Path. Display functions actual fun start. At( x: Double, y: Double) { path. move. To(x. to. Float(), y. to. Float()) } actual fun start. At( x: Double, y: Double) { path. move. To. Point( CGPoint. Make(x, y)) } actual fun quad. To(. . . ) { path. quad. To(. . . ) } actual fun quad. To(. . . ) { path. add. Quad. Curve. To. Point(. . . ) } actual fun cubic. To(. . . ) { path. cubic. To(. . . ) } actual fun cubic. To(. . . ) { path. add. Curve. To. Point(. . . ) }
Android Implementation of Path. Display. draw i. OS Implementation of Path. Display. draw actual fun draw(canvas: Any? ) { val c = canvas!! as Canvas if(line. Color!=0) { paint. set. Color(line. Color) paint. set. Style( Paint. Style. STROKE) paint. set. Stroke. Width( line. Width. to. Float()) c. draw. Path(path, paint) }. . . } actual fun draw(canvas: Any? ) { line. Color? . let { path? . line. Width = line. Width it. set. Stroke() path? . stroke() } . . . }
Invoke from Common Game Code to Draw a Heart Shape fun heart. Path. Display(width: Double, height: Double) : Path. Display { val p = Path. Display() p. start. At(0. 0, height / 4) p. cubic. To(0. 0, -height/12, width/2, height/4) // (1). . . }
Image Load Android Implementation Image Load i. OS Implementation actual fun load(file: String, width: Double, height: Double, asset. Manager: Any? ) { val am = asset. Manager!! as Asset. Manager val filepath = "image/$filename. png" actual fun load(file: String, width: Double, height: Double, asset. Manager: Any? ) { image = UIImage. image. Named("image/$file. png") val size. It = asset. Manager. open(filepath) options. in. Just. Decode. Bounds = true Bitmap. Factory. decode. Stream(size. It, null, options) size. It. close() //calculate sample size, then load image using that val load. It = asset. Manager. open(filepath) options. in. Just. Decode. Bounds = false val bitmap = Bitmap. Factory. decode. Stream(load. It, null, options) load. It. close() //resize image to display size image. Bitmap = Bitmap. create. Scaled. Bitmap(bitmap, sx, sy, true) } //Get size from loaded image var image. Width = 0. 0 var image. Height = 0. 0 image? . size? . use. Contents { image. Width = this. width image. Height = this. height } //resize image to display size (size calculation not shown) val rect = CGRect. Make(0. 0, this. width, this. height) val size = CGSize. Make(this. width, this. height) UIGraphics. Begin. Image. Context(size); image? . draw. In. Rect(rect); image = UIGraphics. Get. Image. From. Current. Image. Context(); UIGraphics. End. Image. Context(); }
Image Draw Android Implementation actual fun draw(matrix: Transform, canvas: Any? ) { val c = canvas!! as Canvas Image Draw i. OS Implementation actual fun draw(matrix: Transform, canvas: Any? ) { var context = UIGraphics. Get. Current. Context() CGContext. Save. GState(context) CGContext. Concat. CTM(context, matrix) image? . draw. At. Point(loc) CGContext. Restore. GState(context) if(matrix. filter. Bitmap || alphavalue<255) { c. draw. Bitmap(image. Bitmap, matrix, paint) } else { c. draw. Bitmap(image. Bitmap, matrix, null) } } }
Transform Android Implementation actual class Transform { Transform i. OS Implementation actual class Transform { internal val matrix: Matrix = Matrix() internal var filter. Bitmap = false; internal var matrix = CGAffine. Transform. Identity. read. Value() actual fun post. Translate(left: Double, top: Double) { matrix. post. Translate(left. to. Float(), top. to. Float()) } actual fun post. Translate(left: Double, top: Double) { var transform = CGAffine. Transform. Make. Translation( left, top) matrix = CGAffine. Transform. Concat( matrix, transform) }
Expects and Actual Loading and drawing images Transforming i. e. rotating, translating, scaling images Drawing text in different fonts Playing audio instructions
Interface implemented in Native
Back button in the game
Game Rendering on Android
Game Rendering on i. OS
Shared Interface in Kotlin interface Game. Player { fun get. Media. Manager(): Media. Manager fun get. Sound. Manager(): Sound. Manager fun get. Asset. Manager() : Any? fun back() fun get. Touch. Events(): List<Touch. Event> fun save. Exercise. State(inc. Mistakes: Float, inc. Time: Int, completed: Boolean) }
Invoke from Multi-platform Game Code to handle User Input abstract class Game(player: Game. Player) { var player: Weak. Ref<Game. Player> init { this. player = Weak. Ref<Game. Player> (player) } //Used later: if(player. get()==null) return; val player = this. player. get()!! if (back. Button. touch_up(event. x, event. y, event. pointer)) { player. get. Media. Manager(). stop() player. back() return }
Back Press Android Implementation class Render. View(context: Context, attrs: Attribute. Set) : Surface. View(context, attrs), Game. Player { Back Press i. OS Implementation public class Screen. View. Controller: UIView. Controller, Game. Player { override fun back() { context. run. On. Ui. Thread { context. on. Back. Pressed() } } } public func back() { dismiss ( animated: true, completion: nil) } }
Game Create Android Implementation class Render. View(context: Context, attrs: Attribute. Set) : Surface. View(context, attrs), Game. Player { Game Create i. OS Implementation public class Screen. View: UIView { var game: Game? = nil weak var player: Game. Player? override public func layout. Subviews() { … self. game = Stamp. Game(player: player, gifts: gifts, instruction: text) … } protected var game = Atomic. Reference<Game>() init { game. set( Stamp. Game(this, gifts, text) } } }
Preventing Memory Leak on i. OS this. player = Weak. Ref <Game. Player> (player)
Common Interface implemented in Native Touch events from native view to the shared game screen Invoking Firebase from shared game screen to save data Accessing application scoped objects from game screen
Two ways of sharing code using Kotlin Native
Source: https: //appadvice. com/appnn/2014/05/meet-cider-a-research-project-that-allows-android-devices-to-run-ios-apps
Lesson 1. No Objects public class Sound. Manager { static private Sound. Manager instance; //Singleton private Sound. Pool m. Sound. Pool; public static void load. Sounds(final Context the. Context, final Set<Integer> sounds. To. Load) { … m. Sound. Pool. load(the. Context, soundid, 1); } private void play. Sound(int index) { m. Sound. Pool. play(index, 1, 1, 1, 0, 1 f); } public void play. Monkey(){play. Sound(monkey); } public void play. Chime(){play. Sound(chime); } }
Sound. Manager creation on Android abstract class Math. Application : android. app. Application() { private lateinit var sound. Manager: Atomic. Reference<Sound. Manager> override fun on. Create() { super. on. Create() sound. Manager = Atomic. Reference( Sound. Manager(assets)) } Sound. Manager creation on i. OS class App. Delegate: UIResponder, UIApplication. Delegate { var sound. Manager: Sound. Manager = Sound. Manager(asset. Manager: nil) }
Shared Interface in Kotlin interface Game. Player { fun get. Media. Manager(): Media. Manager fun get. Sound. Manager(): Sound. Manager fun get. Asset. Manager() : Any? fun back() fun get. Touch. Events(): List<Touch. Event> fun save. Exercise. State(inc. Mistakes: Float, inc. Time: Int, completed: Boolean) }
Get and use the Sound. Manager in the Game open class Clothes. Line. Number. Game(player: Game. Player, internal var exercise: Clothes. Line. Exercise) : Game(player) { fun get. Sound. Manager(): Sound. Manager? { return player. get()? . get. Sound. Manager() } override fun touch. Up(x: Double, y: Double, pointer: Int) { … if (exercise. theme ==Numbe. Line. Theme. DRESS) get. Sound. Manager()? . play. Chime() else if (exercise. theme == Numbe. Line. Theme. MONKEY) get. Sound. Manager()? . play. Monkey(). . . } }
Lesson 2. Array vs. List Initialization of Kotlin Array in Swift: Initialization of Kotlin List in Swift: let gifts = Stdlib. Array( size: 1, init: {_ in Gift(. . . ) } ) let gifts = [ Gift(. . . ) ]
Lesson 3. Initialization Order open class Parent { init. Child() } open fun init. Child() {. . . } } class Child: Parent() { var c: String = "Declare" init { } override fun init. Child() { c = "Initialize" } }
Links Using Surface. View to build 2 D games, Droidcon NY, 2015 https: //www. youtube. com/watch? v=KBkl. J 76 d. D 8 I 12 -part series on porting 2 D educational Games to i. OS using Kotlin Native: https: //medium. com/@anaredmond/cross-platform-game-1 -of-afbb 8 b 16 c 3 f 7 A post with this presentation will be added soon to: https: //medium. com/@anaredmond
THANK YOU AND REMEMBER TO VOTE Ana Redmond @anaredmond #Kotlin. Conf
- Tic tac toe going high going low going criss cross lollipop
- Going beyond the native speaker in language teaching
- Bureaucratic bypass syndrome
- Going through hell
- Microsoft one way redmond
- Annette redmond
- Microsoft research redmond
- Darlene redmond
- Midwest mfg uses a balanced scorecard
- Ed redmond
- Windows hello certificate expired
- Riscv international ceo calista
- Sam johnson park
- Rochdale afc
- Copenhagen charter cos'è
- Todstellreflex
- Eu
- Ems 2018 copenhagen
- Arv copenhagen
- Copenhagen charter cos'è
- Neil shenvi
- Copenhagen school five sectors of security
- Copenhagen golf center
- Dr david galbreath
- Reflections copenhagen
- Copenhagen interpretation
- Securitization copenhagen school
- Arv copenhagen
- Copenhagen
- Internet copenhagen
- Epr paradox
- Royal copenhagen library
- Hera copenhagen
- Workday oms
- Tata motors cto
- By name title
- Hybrid algorithm cto
- Cto
- Finecross vs corsair
- Dts stuck at cto booked
- Ceoceo
- Sfa cto
- Cto forum magazine
- Gaia second wire
- Apa itu cto
- Survey cto
- Barclays digital transformation