contrib/tinks: intro the android client
authorAlexis Laferrière <alexis.laf@xymus.net>
Thu, 30 Jul 2015 12:21:43 +0000 (08:21 -0400)
committerAlexis Laferrière <alexis.laf@xymus.net>
Sun, 2 Aug 2015 16:25:24 +0000 (12:25 -0400)
Signed-off-by: Alexis Laferrière <alexis.laf@xymus.net>

contrib/tinks/Makefile
contrib/tinks/res/.gitignore [new file with mode: 0644]
contrib/tinks/src/client/android_client.nit [new file with mode: 0644]
contrib/tinks/src/client/controls.nit [new file with mode: 0644]

index 5bee72e..b22453a 100644 (file)
@@ -31,10 +31,18 @@ bin/server: src/server/server_serialize.nit $(shell ../../bin/nitls -M src/serve
 src/server/server_serialize.nit: $(shell ../../bin/nitls -M src/server/dedicated.nit)
        ../../bin/nitserial -o src/server/server_serialize.nit src/server/dedicated.nit
 
+# Android
+bin/tinks.apk: assets/images/drawing.png src/client/client_serialize.nit res/drawable-ldpi/icon.png $(shell ../../bin/nitls -M src/client/android_client.nit)
+       ../../bin/nitc -o bin/tinks.apk src/client/android_client.nit -m src/client/client_serialize.nit --compile-dir nit_compile
+       adb install -r bin/tinks.apk
+
+res/drawable-ldpi/icon.png: art/icon.svg
+       ../inkscape_tools/bin/svg_to_icons art/icon.svg --android --out res/
+
 # Archive
-pub: assets/images/drawing.png src/client/client_serialize.nit
+pub: assets/images/drawing.png src/client/client_serialize.nit bin/tinks.apk
        ../../bin/nitc --no-stacktrace -o bin/tinks src/client/linux_client.nit -m src/client/client_serialize.nit
        tar -czvf bin/tinks.tar.gz bin/tinks assets/
-       scp bin/tinks.tar.gz xymus.net:/var/www/pub/
+       scp bin/tinks.tar.gz bin/tinks.apk xymus.net:/var/www/pub/
 
 .PHONY: pub
diff --git a/contrib/tinks/res/.gitignore b/contrib/tinks/res/.gitignore
new file mode 100644 (file)
index 0000000..72e8ffc
--- /dev/null
@@ -0,0 +1 @@
+*
diff --git a/contrib/tinks/src/client/android_client.nit b/contrib/tinks/src/client/android_client.nit
new file mode 100644 (file)
index 0000000..f00fd34
--- /dev/null
@@ -0,0 +1,183 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Android client with a joystick
+module android_client is
+       app_name "Tinks!"
+       app_namespace "net.xymus.tinks"
+       android_manifest """<uses-permission android:name="android.permission.INTERNET" />"""
+       android_api_target 10
+end
+
+import mnit_android
+import android::audio
+import android::vibration
+import android::landscape
+
+intrude import client
+import controls
+
+redef class App
+
+       # Tank direction control
+       var joystick = new Joystick is lazy
+
+       redef var controls = new Array[Control].with_items(joystick) is lazy
+
+       redef fun input(event)
+       do
+               var local_player = context.local_player
+               var local_tank = local_tank
+               if local_player != null and local_tank != null and event isa ControlEvent then
+                       local_player.orders.add new TankDirectionOrder(local_tank, joystick.value_x, joystick.value_y)
+                       return true
+               end
+
+               if event isa AndroidKeyEvent then
+                       if event.is_back_key then
+                               quit = true
+                               native_activity.finish
+                               return false
+                       end
+               end
+
+               return super
+       end
+end
+
+redef class ExplosionEvent
+       redef fun client_react(display, turn)
+       do
+               super
+
+               var local_tank = app.local_tank
+               var d = 20
+               if local_tank != null then
+                       d = local_tank.pos.dist(pos).to_i
+                       d = 500 - d*20
+                       d = d.max(10)
+               end
+
+               app.vibrator.vibrate d
+       end
+end
+
+# On-demand joystick that popups up when tapping the left border of the screen
+class Joystick
+       super Control
+
+       # Current position of the joystick from its center on the X axis, in `[-1.0..1.0]`
+       var value_x = 0.0
+
+       # Current position of the joystick from its center on the Y axis, in `[-1.0..1.0]`
+       var value_y = 0.0
+
+       # Deadzone at the center of the joystick where the values are set at 0.0
+       var deadzone = 0.3
+
+       # Position of the center of the joystick, set at where the screen is first tapped
+       var center: nullable ScreenPos
+
+       # Position of the top of the joystick, the one that follows the finger
+       var handle: nullable ScreenPos
+
+       # Id of the pointer/finger that triggered the joystick
+       var captured_pointer: Int = -1
+
+       # Image of the left border
+       var img_zone: Image = app.assets.drawing.joystick_zone
+
+       # Image of the joystick base
+       var img_back: Image = app.assets.drawing.joystick_back
+
+       # Image of the top of the joystick
+       var img_handle: Image = app.assets.drawing.joystick_handle
+
+       # Radius where the top of the joystick can move around the center
+       var radius: Float = img_back.width.to_f / 2.0 - 4.0
+
+       # Width of the left border used to trigger the joystick
+       var capture_width = 400.0
+
+       redef fun draw(display)
+       do
+               display.blit_stretched(img_zone,
+                       0, -128,
+                       0, display.height+128,
+                       capture_width, display.height+128,
+                       capture_width, -128)
+
+               var center = center
+               var handle = handle
+               if center != null and handle != null then
+                       img_back.scale = 1.0
+                       img_handle.scale = 1.0
+                       display.blit_centered(img_back, center.x, center.y)
+                       display.blit_centered(img_handle, handle.x, handle.y)
+               end
+       end
+
+       redef fun input(event)
+       do
+               if event isa AndroidPointerEvent then
+                       var center = center
+                       if center == null then
+                               # New joystick?
+                               print "New joystick? {event.just_went_down} {event.x} < {capture_width}"
+                               if event.just_went_down and
+                                  event.x < capture_width then
+                                       # Capture it!
+                                       self.center = new ScreenPos(event.x, event.y)
+                                       self.captured_pointer = event.pointer_id
+                                       self.handle = center
+                                       return true
+                               end
+                       else
+                               # Already down
+
+                               # Is it the finger already on the joystick?
+                               if captured_pointer != event.pointer_id then return false
+
+                               if event.depressed then
+                                       self.center = null
+                                       self.handle = null
+                                       self.value_x = 0.0
+                                       self.value_y = 0.0
+                               else
+                                       # Update values
+                                       var dx = center.x - event.x
+                                       var dy = center.y - event.y # This is inverted, as is the input
+                                       var d = dx.hypot_with(dy)
+                                       if d < deadzone then
+                                               self.value_x = 0.0
+                                               self.value_y = 0.0
+                                               self.handle = center
+                                       else
+                                               var a = atan2(dx, dy)+pi/2.0
+                                               self.value_x = a.cos
+                                               self.value_y = a.sin
+
+                                               if d > radius then d = radius
+                                               self.handle = new ScreenPos(center.x+a.cos*d, center.y-a.sin*d)
+                                       end
+                               end
+
+                               app.input new ControlEvent(self)
+                               return true
+                       end
+               end
+
+               return false
+       end
+end
diff --git a/contrib/tinks/src/client/controls.nit b/contrib/tinks/src/client/controls.nit
new file mode 100644 (file)
index 0000000..c3a186f
--- /dev/null
@@ -0,0 +1,59 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# On-screen alternative controls
+module controls
+
+import client
+
+redef class App
+
+       # All active UI controls
+       fun controls: Array[Control] is abstract
+
+       redef fun frame_core(display)
+       do
+               super
+
+               for control in controls do control.draw(display)
+       end
+
+       redef fun input(event)
+       do
+               for control in controls do
+                       var hit = control.input(event)
+                       if hit then return true
+               end
+
+               return super
+       end
+end
+
+# An event raised by a `Control`
+class ControlEvent
+       super InputEvent
+
+       # Sender control
+       var sender: Control
+end
+
+# UI control
+abstract class Control
+
+       # Draw `self` to `display`
+       fun draw(display: Display) do end
+
+       # Intercept and act upon events concerving `self`
+       fun input(event: InputEvent): Bool do return false
+end