Commit 77884575 authored by deregges's avatar deregges

Inlined Game into GameActivity

Fixed rotation input
parent 8f76ed98
package io.ender.pathchaser
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Canvas
import android.view.SurfaceView
@SuppressLint("ViewConstructor")
class DelegatingSurfaceView(private val ctx: Context, private val pDraw: (Canvas) -> Unit) : SurfaceView(ctx) {
override fun draw(canvas: Canvas) {
super.draw(canvas)
pDraw(canvas)
}
}
package io.ender.pathchaser
import android.app.Activity
import android.app.Dialog
import android.os.Bundle
import android.view.View
......@@ -13,7 +14,7 @@ import splitties.resources.appStr
import splitties.views.design.string
import splitties.views.design.text
class EndGameDialog private constructor(a: GameActivity, private val score: Int) : Dialog(a) {
class EndGameDialog private constructor(a: Activity, private val score: Int) : Dialog(a) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
requestWindowFeature(Window.FEATURE_NO_TITLE)
......@@ -62,7 +63,7 @@ class EndGameDialog private constructor(a: GameActivity, private val score: Int)
}
companion object {
fun show(a: GameActivity, score: Int) {
fun show(a: Activity, score: Int) {
val diag = EndGameDialog(a, score)
val layout = WindowManager.LayoutParams()
......
package io.ender.pathchaser
import android.annotation.SuppressLint
import android.graphics.*
import android.util.DisplayMetrics
import android.view.MotionEvent
import android.view.SurfaceHolder
import android.view.SurfaceView
import io.ender.pathchaser.paths.PathManager
import io.ender.pathchaser.util.Constants
import io.ender.pathchaser.util.Preferences
import io.ender.pathchaser.util.intersects
import java.io.Serializable
@SuppressLint("ViewConstructor")
class Game(@Transient private val gameActivity: GameActivity) : SurfaceView(gameActivity),
SurfaceHolder.Callback, Serializable {
var ticks = 0
private set
var collectedCoins = 0
private var paused = true
@Transient
private lateinit var pathManager: PathManager
@Transient
private lateinit var player: Player
@Transient
val size = PointF().apply {
DisplayMetrics().also { gameActivity.windowManager.defaultDisplay.getRealMetrics(it) }
.let { set(it.widthPixels.toFloat(), it.heightPixels.toFloat()) }
}
var speed: Float = Preferences.difficulty.initialSpeed
private var background = Color.BLACK
private val highscore
get() = collectedCoins * Constants.pointsCoinSimple + (ticks * Constants.pointsPerTick).toInt()
init {
holder.addCallback(this)
isFocusable = true
}
override fun surfaceCreated(surfaceHolder: SurfaceHolder) {
if (!(this::pathManager.isInitialized))
pathManager = PathManager(this)
if (!(this::player.isInitialized)) {
player = Player(pathManager)
player.setPos((width / 2).toFloat(), (height / 2).toFloat())
pathManager.player = player
}
}
override fun surfaceChanged(surfaceHolder: SurfaceHolder, format: Int, width: Int, height: Int) = Unit
override fun surfaceDestroyed(surfaceHolder: SurfaceHolder) = Unit
@SuppressLint("ClickableViewAccessibility" /* performClick would be useless */)
override fun onTouchEvent(event: MotionEvent): Boolean {
if (Preferences.inputMethod == Constants.InputMethod.TOUCH) {
when (event.action) {
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_MOVE -> {
if (paused) {
if (player.intersects(Path().apply {
addRect(
event.x,
event.y + Constants.playerOffset.y,
event.x + 1,
event.y + 1 + Constants.playerOffset.y,
Path.Direction.CW
)
})) {
player.setTouchPos(event.x, event.y)
paused = false
}
} else {
player.setTouchPos(event.x, event.y)
}
}
MotionEvent.ACTION_UP -> {
paused = true
}
}
}
return true
}
fun update() {
if (!paused) {
player.update()
pathManager.update()
if (ticks % 30 == 0)
speed += Preferences.difficulty.speedPerSecond
if (player.invincible)
blink()
ticks++
}
}
override fun draw(canvas: Canvas) {
super.draw(canvas)
// this is background color
canvas.drawColor(background)
pathManager.draw(canvas)
player.draw(canvas, player.getPaint())
canvas.drawText("Lives: " + player.lives, 0f, 40f, Paint().apply {
color = Color.CYAN
textSize = 50f
})
canvas.drawText("Score: $highscore", 0f, 80f, Paint().apply {
color = Color.CYAN
textSize = 50f
})
canvas.drawRect(RectF(0f, size.y - 10, size.x, size.y).apply { }, Paint().apply { color = Color.BLACK })
if (player.invincible) {
val timePassed = ticks - player.invincibleStart
val steps = size.x / (Constants.invincibleSec * Constants.targetFPS)
canvas.drawRect(
RectF(0f, size.y - 8, size.x - timePassed * steps, size.y).apply { },
Paint().apply { color = Color.RED })
}
}
private fun blink() {
if (ticks % (Constants.targetFPS * Constants.blinkMs / 1000).toInt() == 0) {
val iteration =
(ticks - player.invincibleStart - ((ticks - player.invincibleStart) % Constants.targetFPS)) / Constants.targetFPS
if (iteration < Constants.invincibleSec) {
background = if ((iteration + 1) % 2 == 0) Constants.blinkColor else Color.BLACK
} else {
background = Color.BLACK
player.invincible = false
}
}
}
fun gameEnd() {
gameActivity.gameEnd()
gameActivity.runOnUiThread {
EndGameDialog.show(gameActivity, highscore)
}
}
fun orientationUpdate(orientation: FloatArray) {
when (Preferences.inputMethod) {
Constants.InputMethod.ROTATION -> {
player.delta.x = orientation[2]
player.delta.y = orientation[1]
}
Constants.InputMethod.ROTATION2D -> {
player.delta.x = orientation[2]
}
Constants.InputMethod.TOUCH -> Unit
}
}
}
package io.ender.pathchaser
import android.annotation.SuppressLint
import android.graphics.*
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import android.os.Bundle
import android.util.DisplayMetrics
import android.view.MotionEvent
import android.view.SurfaceView
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import io.ender.pathchaser.paths.PathManager
import io.ender.pathchaser.util.Constants
import io.ender.pathchaser.util.Preferences
import io.ender.pathchaser.util.intersects
import splitties.systemservices.sensorManager
import kotlin.math.PI
class GameActivity : AppCompatActivity(), SensorEventListener {
var ticks = 0
private set
var collectedCoins = 0
private var paused = Preferences.inputMethod == Constants.InputMethod.TOUCH
private val pathManager: PathManager by lazy {
PathManager(this)
}
val player: Player by lazy {
Player(pathManager).also {
it.setPos(size.x / 2, size.y / 2)
}
}
val size by lazy {
PointF().apply {
DisplayMetrics().also { windowManager.defaultDisplay.getRealMetrics(it) }
.let { set(it.widthPixels.toFloat(), it.heightPixels.toFloat()) }
}
}
var speed: Float = Preferences.difficulty.initialSpeed
private var background = Color.BLACK
private val highscore
get() = collectedCoins * Constants.pointsCoinSimple + (ticks * Constants.pointsPerTick).toInt()
private var gameThread: GameThread? = null
lateinit var game: Game
private val surface: SurfaceView by lazy {
DelegatingSurfaceView(this, this::draw).apply { isFocusable = true }
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
game = Game(this)
setContentView(game)
setContentView(surface)
}
override fun onPause() {
super.onPause()
sensorManager.unregisterListener(this)
gameThread?.interrupt()
gameThread = null
sensorManager.unregisterListener(this)
}
override fun onResume() {
......@@ -39,7 +71,7 @@ class GameActivity : AppCompatActivity(), SensorEventListener {
)
else -> Unit
}
gameThread = GameThread(game)
gameThread = GameThread(this)
}
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) = Unit
......@@ -50,18 +82,16 @@ class GameActivity : AppCompatActivity(), SensorEventListener {
orientation[0] /= PI.toFloat()
orientation[1] *= 2 / PI.toFloat()
orientation[2] *= 2 / PI.toFloat()
game.orientationUpdate(orientation)
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putSerializable("game", game)
}
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState)
game = savedInstanceState.get("game") as Game
when (Preferences.inputMethod) {
Constants.InputMethod.ROTATION -> {
player.delta.x = orientation[2] * 50
player.delta.y = -orientation[1] * 50
}
Constants.InputMethod.ROTATION2D -> {
player.delta.x = orientation[2] * 50
}
Constants.InputMethod.TOUCH -> Unit
}
}
override fun onWindowFocusChanged(hasFocus: Boolean) { // fullscreen trick
......@@ -78,5 +108,101 @@ class GameActivity : AppCompatActivity(), SensorEventListener {
fun gameEnd() {
gameThread?.interrupt()
EndGameDialog.show(this, highscore)
}
fun update() {
fun blink() {
if (ticks % (Constants.targetFPS * Constants.blinkMs / 1000).toInt() == 0) {
val iteration =
(ticks - player.invincibleStart - ((ticks - player.invincibleStart) % Constants.targetFPS)) / Constants.targetFPS
if (iteration < Constants.invincibleSec) {
background = if ((iteration + 1) % 2 == 0) Constants.blinkColor else Color.BLACK
} else {
background = Color.BLACK
player.invincible = false
}
}
}
if (!paused) {
player.update()
pathManager.update()
if (ticks % 30 == 0)
speed += Preferences.difficulty.speedPerSecond
if (player.invincible)
blink()
ticks++
}
}
@SuppressLint("ClickableViewAccessibility" /* performClick would be useless */)
override fun onTouchEvent(event: MotionEvent): Boolean {
if (Preferences.inputMethod == Constants.InputMethod.TOUCH) {
when (event.action) {
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_MOVE -> {
if (paused) {
if (player.intersects(Path().apply {
addRect(
event.x,
event.y + Constants.playerOffset.y,
event.x + 1,
event.y + 1 + Constants.playerOffset.y,
Path.Direction.CW
)
})) {
player.setTouchPos(event.x, event.y)
paused = false
}
} else {
player.setTouchPos(event.x, event.y)
}
}
MotionEvent.ACTION_UP -> {
paused = true
}
}
}
return true
}
fun draw() {
val canvas = surface.holder.lockCanvas()
if (canvas != null) {
try {
synchronized(canvas) {
surface.draw(canvas)
}
} catch (e: Exception) {
e.printStackTrace()
}
surface.holder.unlockCanvasAndPost(canvas)
}
}
private fun draw(canvas: Canvas) {
// this is background color
canvas.drawColor(background)
pathManager.draw(canvas)
player.draw(canvas, player.getPaint())
canvas.drawText("Lives: " + player.lives, 0f, 40f, Paint().apply {
color = Color.CYAN
textSize = 50f
})
canvas.drawText("Score: $highscore", 0f, 80f, Paint().apply {
color = Color.CYAN
textSize = 50f
})
canvas.drawRect(RectF(0f, size.y - 10, size.x, size.y).apply { }, Paint().apply { color = Color.BLACK })
if (player.invincible) {
val timePassed = ticks - player.invincibleStart
val steps = size.x / (Constants.invincibleSec * Constants.targetFPS)
canvas.drawRect(
RectF(0f, size.y - 8, size.x - timePassed * steps, size.y).apply { },
Paint().apply { color = Color.RED })
}
}
}
......@@ -2,7 +2,7 @@ package io.ender.pathchaser
import io.ender.pathchaser.util.Constants
class GameThread(private val game: Game) : Thread() {
class GameThread(private val game: GameActivity) : Thread() {
companion object {
private const val targetTime = 1000 / Constants.targetFPS
}
......@@ -26,19 +26,7 @@ class GameThread(private val game: Game) : Thread() {
val start = System.currentTimeMillis()
game.update()
val canvas = game.holder.lockCanvas()
if (canvas != null) {
try {
synchronized(canvas) {
game.draw(canvas)
}
} catch (e: Exception) {
e.printStackTrace()
}
game.holder.unlockCanvasAndPost(canvas)
}
game.draw()
val waitTime = targetTime - (System.currentTimeMillis() - start)
if (waitTime > 0) sleep(waitTime)
......
......@@ -7,7 +7,6 @@ import io.ender.pathchaser.util.Constants
import io.ender.pathchaser.util.Preferences
import io.ender.pathchaser.util.contains
import io.ender.pathchaser.util.intersects
import kotlin.concurrent.thread
class Player(pm: PathManager) : Sprite(pm) {
var lives = Preferences.difficulty.lives
......@@ -18,7 +17,7 @@ class Player(pm: PathManager) : Sprite(pm) {
invincible = true
} else {
field = 0
thread { pm.game.gameEnd() }
pm.game.runOnUiThread { pm.game.gameEnd() }
}
}
var invincibleStart = -1
......@@ -95,7 +94,6 @@ class Player(pm: PathManager) : Sprite(pm) {
}
shields.remove(rs)
pos.x += delta.x
pos.y += delta.y
setPos(pos.x + delta.x, pos.y + delta.y)
}
}
......@@ -11,16 +11,16 @@ class Shields(pm: PathManager, p: PointF) : Collectible(pm, p) {
override fun getPaint() = Paint().apply { color = Color.GREEN }
override fun trigger() {
pm.player.shields.removeAll{true}
pm.game.player.shields.removeAll { true }
for (i in 1..4)
pm.player.shields.add(
Shields(pm, PointF(pm.player.pos.x, pm.player.pos.y + Constants.playerRadius *1.5f))
pm.game.player.shields.add(
Shields(pm, PointF(pm.game.player.pos.x, pm.game.player.pos.y + Constants.playerRadius * 1.5f))
.apply {
transform(Matrix().apply {
setRotate(
(i * 360 / 4).toFloat(),
pm.player.pos.x,
pm.player.pos.y
pm.game.player.pos.x,
pm.game.player.pos.y
)
})
}
......
......@@ -37,8 +37,8 @@ class CirclePath(pm: PathManager, start: PointF, r: Float, enemyNumber: Int = 0)
for (e in enemies) {
e.transform(Matrix().apply { setRotate(pm.game.speed, start.x, start.y) })
e.detransform()
if (e.intersects(pm.player)) {
pm.player.lives--
if (e.intersects(pm.game.player)) {
pm.game.player.lives--
}
e.transform()
}
......
......@@ -3,18 +3,16 @@ package io.ender.pathchaser.paths
import android.graphics.Canvas
import android.graphics.PointF
import android.graphics.Region
import io.ender.pathchaser.Game
import io.ender.pathchaser.GameActivity
import io.ender.pathchaser.Log
import io.ender.pathchaser.Player
import io.ender.pathchaser.collectibles.*
import io.ender.pathchaser.enemys.Enemy
import io.ender.pathchaser.util.Constants
import java.io.Serializable
class PathManager(val game: Game) : Serializable {
class PathManager(val game: GameActivity) : Serializable {
private val paths: MutableList<Pathway> = mutableListOf()
fun getPaths(): List<Pathway> = paths
lateinit var player: Player
var degreesRotated = 0f
var degreesToRotate = 0f
......@@ -47,7 +45,7 @@ class PathManager(val game: Game) : Serializable {
for (path in paths) {
path.update()
path.move(0f, game.speed)
path.collect(player)?.trigger()
path.collect(game.player)?.trigger()
}
if (!paths.first().isOnSurface(Region(0, 0, game.size.x.toInt(), game.size.y.toInt()))) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment