Executando verificação de segurança...
-2

Tutorial: Jogo de corrida mobile (android studio)

Hello World

Para começar é preciso um 'novo projeto' com uma activity em branco.
packageName square.motor

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
	<application
		android:allowBackup="true"
		android:icon="@drawable/carro"
		android:label="square motor">
		<activity
			android:name=".MainActivity"
			android:exported="true"
			android:screenOrientation="landscape"
			android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen">
			<intent-filter>
				<action android:name="android.intent.action.MAIN"/>
				<category android:name="android.intent.category.LAUNCHER"/>
			</intent-filter>
		</activity>
	</application>
</manifest>

Pasta res

Exclua a pasta res e coloque esta pasta (descompactada) no mesmo lugar com o mesmo nome.

MainActivity

package square.motor
import android.app.Activity
import android.os.Bundle
class MainActivity : Activity() {
	public override fun onCreate(savedInstanceState: Bundle?) {
		super.onCreate(savedInstanceState)
		setContentView(SquareMotor(this))
	}
}

Base da classe principal

package square.motor
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Matrix
import android.graphics.Paint
import android.graphics.Typeface
import android.view.MotionEvent
import android.view.View
import java.util.Timer
import java.util.TimerTask
class SquareMotor(context: Context?) : View(context) {
}

Para exibir as imagens no tamanho correto

	var btnPrevious: Bitmap? = null
	var btnNext: Bitmap? = null
	var logo: Bitmap? = null
	var btncw: Bitmap? = null
	var btnccw: Bitmap? = null
	var star: Bitmap? = null
	var opaqueStar: Bitmap? = null
	var menu: Bitmap? = null
	var car = arrayOfNulls<Bitmap>(36)
	var tile = arrayOfNulls<Bitmap>(21)
	var numbers = arrayOfNulls<Bitmap>(6)
	var miniTiles = arrayOfNulls<Bitmap>(21)

Algumas imagens são rotacionadas

	override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
		super.onSizeChanged(w, h, oldw, oldh)
		screenWidth = w.toFloat()
		screenHeight = h.toFloat()
		fractionScreenSize = screenHeight / 600
		stage = 0
		val typeface = Typeface.create(Typeface.SERIF, Typeface.BOLD)
		paint.typeface = typeface
		paint.textAlign = Paint.Align.CENTER
		val r = resources
		logo = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(r, R.drawable.logo), Math.ceil((645 * fractionScreenSize).toDouble()).toInt(), Math.ceil((450 * fractionScreenSize).toDouble()).toInt(), true)
		btnccw = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(r, R.drawable.ccw), Math.ceil((120 * fractionScreenSize).toDouble()).toInt(), Math.ceil((120 * fractionScreenSize).toDouble()).toInt(), true)
		btncw = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(r, R.drawable.cw), Math.ceil((120 * fractionScreenSize).toDouble()).toInt(), Math.ceil((120 * fractionScreenSize).toDouble()).toInt(), true)
		btnPrevious = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(r, R.drawable.prev), Math.ceil((200 * fractionScreenSize).toDouble()).toInt(), Math.ceil((200 * fractionScreenSize).toDouble()).toInt(), true)
		btnNext = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(r, R.drawable.prox), Math.ceil((200 * fractionScreenSize).toDouble()).toInt(), Math.ceil((200 * fractionScreenSize).toDouble()).toInt(), true)
		car[0] = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(r, R.drawable.carro), Math.ceil((72 * fractionScreenSize).toDouble()).toInt(), Math.ceil((72 * fractionScreenSize).toDouble()).toInt(), true)
		numbers[1] = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(r, R.drawable.n1), Math.ceil((240 * fractionScreenSize).toDouble()).toInt(), Math.ceil((240 * fractionScreenSize).toDouble()).toInt(), true)
		numbers[2] = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(r, R.drawable.n2), Math.ceil((240 * fractionScreenSize).toDouble()).toInt(), Math.ceil((240 * fractionScreenSize).toDouble()).toInt(), true)
		numbers[3] = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(r, R.drawable.n3), Math.ceil((240 * fractionScreenSize).toDouble()).toInt(), Math.ceil((240 * fractionScreenSize).toDouble()).toInt(), true)
		numbers[4] = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(r, R.drawable.n4), Math.ceil((240 * fractionScreenSize).toDouble()).toInt(), Math.ceil((240 * fractionScreenSize).toDouble()).toInt(), true)
		numbers[5] = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(r, R.drawable.n5), Math.ceil((240 * fractionScreenSize).toDouble()).toInt(), Math.ceil((240 * fractionScreenSize).toDouble()).toInt(), true)
		var matrix: Matrix
		for (x in 1..35) {
			matrix = Matrix()
			matrix.postRotate((x * 10).toFloat())
			car[x] = Bitmap.createBitmap(car[0]!!, 0, 0, Math.ceil((72 * fractionScreenSize).toDouble()).toInt(), Math.ceil((72 * fractionScreenSize).toDouble()).toInt(), matrix, true)
		}
		tile[1] = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(r, R.drawable.linha), Math.ceil((200 * fractionScreenSize).toDouble()).toInt(), Math.ceil((200 * fractionScreenSize).toDouble()).toInt(), true)
		matrix = Matrix()
		matrix.postRotate(180f)
		tile[2] = Bitmap.createBitmap(tile[1]!!, 0, 0, Math.ceil((200 * fractionScreenSize).toDouble()).toInt(), Math.ceil((200 * fractionScreenSize).toDouble()).toInt(), matrix, true)
		matrix = Matrix()
		matrix.postRotate(90f)
		tile[3] = Bitmap.createBitmap(tile[1]!!, 0, 0, Math.ceil((200 * fractionScreenSize).toDouble()).toInt(), Math.ceil((200 * fractionScreenSize).toDouble()).toInt(), matrix, true)
		matrix = Matrix()
		matrix.postRotate(270f)
		tile[4] = Bitmap.createBitmap(tile[1]!!, 0, 0, Math.ceil((200 * fractionScreenSize).toDouble()).toInt(), Math.ceil((200 * fractionScreenSize).toDouble()).toInt(), matrix, true)
		tile[5] = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(r, R.drawable.curvahorario), Math.ceil((200 * fractionScreenSize).toDouble()).toInt(), Math.ceil((200 * fractionScreenSize).toDouble()).toInt(), true)
		tile[6] = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(r, R.drawable.curvaantihorario), Math.ceil((200 * fractionScreenSize).toDouble()).toInt(), Math.ceil((200 * fractionScreenSize).toDouble()).toInt(), true)
		matrix = Matrix()
		matrix.postRotate(90f)
		tile[7] = Bitmap.createBitmap(tile[5]!!, 0, 0, Math.ceil((200 * fractionScreenSize).toDouble()).toInt(), Math.ceil((200 * fractionScreenSize).toDouble()).toInt(), matrix, true)
		matrix = Matrix()
		matrix.postRotate(90f)
		tile[8] = Bitmap.createBitmap(tile[6]!!, 0, 0, Math.ceil((200 * fractionScreenSize).toDouble()).toInt(), Math.ceil((200 * fractionScreenSize).toDouble()).toInt(), matrix, true)
		matrix = Matrix()
		matrix.postRotate(180f)
		tile[9] = Bitmap.createBitmap(tile[5]!!, 0, 0, Math.ceil((200 * fractionScreenSize).toDouble()).toInt(), Math.ceil((200 * fractionScreenSize).toDouble()).toInt(), matrix, true)
		matrix = Matrix()
		matrix.postRotate(180f)
		tile[10] = Bitmap.createBitmap(tile[6]!!, 0, 0, Math.ceil((200 * fractionScreenSize).toDouble()).toInt(), Math.ceil((200 * fractionScreenSize).toDouble()).toInt(), matrix, true)
		matrix = Matrix()
		matrix.postRotate(270f)
		tile[11] = Bitmap.createBitmap(tile[5]!!, 0, 0, Math.ceil((200 * fractionScreenSize).toDouble()).toInt(), Math.ceil((200 * fractionScreenSize).toDouble()).toInt(), matrix, true)
		matrix = Matrix()
		matrix.postRotate(270f)
		tile[12] = Bitmap.createBitmap(tile[6]!!, 0, 0, Math.ceil((200 * fractionScreenSize).toDouble()).toInt(), Math.ceil((200 * fractionScreenSize).toDouble()).toInt(), matrix, true)
		tile[13] = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(r, R.drawable.inicio), Math.ceil((200 * fractionScreenSize).toDouble()).toInt(), Math.ceil((200 * fractionScreenSize).toDouble()).toInt(), true)
		tile[14] = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(r, R.drawable.chegada), Math.ceil((200 * fractionScreenSize).toDouble()).toInt(), Math.ceil((200 * fractionScreenSize).toDouble()).toInt(), true)
		matrix = Matrix()
		matrix.postRotate(270f)
		tile[15] = Bitmap.createBitmap(tile[13]!!, 0, 0, Math.ceil((200 * fractionScreenSize).toDouble()).toInt(), Math.ceil((200 * fractionScreenSize).toDouble()).toInt(), matrix, true)
		matrix = Matrix()
		matrix.postRotate(180f)
		tile[16] = Bitmap.createBitmap(tile[13]!!, 0, 0, Math.ceil((200 * fractionScreenSize).toDouble()).toInt(), Math.ceil((200 * fractionScreenSize).toDouble()).toInt(), matrix, true)
		matrix = Matrix()
		matrix.postRotate(90f)
		tile[17] = Bitmap.createBitmap(tile[13]!!, 0, 0, Math.ceil((200 * fractionScreenSize).toDouble()).toInt(), Math.ceil((200 * fractionScreenSize).toDouble()).toInt(), matrix, true)
		matrix = Matrix()
		matrix.postRotate(90f)
		tile[18] = Bitmap.createBitmap(tile[14]!!, 0, 0, Math.ceil((200 * fractionScreenSize).toDouble()).toInt(), Math.ceil((200 * fractionScreenSize).toDouble()).toInt(), matrix, true)
		matrix = Matrix()
		matrix.postRotate(180f)
		tile[19] = Bitmap.createBitmap(tile[14]!!, 0, 0, Math.ceil((200 * fractionScreenSize).toDouble()).toInt(), Math.ceil((200 * fractionScreenSize).toDouble()).toInt(), matrix, true)
		matrix = Matrix()
		matrix.postRotate(270f)
		tile[20] = Bitmap.createBitmap(tile[14]!!, 0, 0, Math.ceil((200 * fractionScreenSize).toDouble()).toInt(), Math.ceil((200 * fractionScreenSize).toDouble()).toInt(), matrix, true)
		star = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(resources, R.drawable.star), (50 * fractionScreenSize).toInt(), (50 * fractionScreenSize).toInt(), true)
		opaqueStar = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(resources, R.drawable.notstar), (50 * fractionScreenSize).toInt(), (50 * fractionScreenSize).toInt(), true)
		menu = Bitmap.createScaledBitmap(BitmapFactory.decodeResource(resources, R.drawable.menu), (64 * fractionScreenSize).toInt(), (64 * fractionScreenSize).toInt(), true)
		miniTilesSize = screenHeight / 18
		for (x in 1..20)
			miniTiles[x] = Bitmap.createScaledBitmap(tile[x]!!, miniTilesSize.toInt(), miniTilesSize.toInt(), true)
//		Timer().schedule(gameloop, 0, 10)
	}

O método onDraw que exibe as imagens

	override fun onDraw(canvas: Canvas) {
		super.onDraw(canvas)
		paint.color = Color.rgb(255, 255, 255)
		canvas.drawPaint(paint)
		paint.color = Color.rgb(0, 0, 0)
		if (time == -5) //splashscreen
		//...
		else if (time == -4) //tela 'passou de fase'
		//...
		else if (time == -3) //tela 'zerou'
		//...
		else if (time == -2) //tela 'não conseguiu'
		//...
		else if (time == -1) { //tela menu
		//desenha minimapa
			for (y in course[stage].indices)
				for (x in course[stage][y].indices)
					if (course[stage][y][x] != 0)
						canvas.drawBitmap(miniTiles[course[stage][y][x]]!!, screenWidth / 2 - course[stage][y].size * miniTilesSize / 2 + x * miniTilesSize, screenHeight / 2 - course[stage].size * miniTilesSize / 2 + y * miniTilesSize, paint)
		//desenha estrela
			canvas.drawBitmap(star!!, screenWidth / 2 - 75 * fractionScreenSize, (screenHeight * 0.02).toFloat(), paint)
		//desenha botao para mudar de fase
			canvas.drawBitmap(btnPrevious!!, 40 * fractionScreenSize, (screenHeight - 300) / 2 * fractionScreenSize, paint)
	}
	else if (time > 0) { //tela gameplay
			//desenha a pista
			for (y in course[stage].indices)
				for (x in course[stage][y].indices)
					if (course[stage][y][x] != 0)
						canvas.drawBitmap(tile[course[stage][y][x]]!!, carPositionX + x * 200 * fractionScreenSize + screenWidth / 2, carPositionY + y * 200 * fractionScreenSize + screenHeight / 2, paint)
			//desenha o carro no angulo certo
			canvas.drawBitmap(car[angle3 / 3]!!, (screenWidth - car[angle3 / 3]!!.width) / 2, (screenHeight - car[angle3 / 3]!!.height) / 2, paint)
			//desenha o botão de virar
			paint.color = Color.rgb(127, 127, 127)
			if (ccwPressed) {
				canvas.drawCircle(80 * fractionScreenSize, screenHeight - 80 * fractionScreenSize, 58 * fractionScreenSize, paint)
				canvas.drawBitmap(btnccw!!, 20 * fractionScreenSize, screenHeight - 140 * fractionScreenSize, paint)
			}
		}
course[stage] é um array de int que representa o traçado da fase.
	var course = arrayOf(
		arrayOf(intArrayOf(12, 4, 6), intArrayOf(2, 0, 1), intArrayOf(2, 0, 1), intArrayOf(2, 0, 1), intArrayOf(2, 0, 1), intArrayOf(2, 0, 1), intArrayOf(19, 0, 16)),
		\\ ...
		\\ mais mapa das fases
		\\ ...
		\\ (acesse o tutorial no site original)
		arrayOf(intArrayOf(12, 4, 6, 0, 12, 4, 4, 4, 6), intArrayOf(2, 0, 1, 0, 2, 0, 0, 0, 1), intArrayOf(2, 0, 9, 4, 7, 0, 11, 3, 8), intArrayOf(2, 0, 0, 0, 0, 0, 1, 0, 0), intArrayOf(10, 3, 5, 0, 14, 0, 9, 4, 6), intArrayOf(0, 0, 2, 0, 1, 0, 0, 0, 1), intArrayOf(12, 4, 7, 0, 1, 0, 11, 3, 8), intArrayOf(2, 0, 0, 0, 1, 0, 1, 0, 0), intArrayOf(10, 3, 3, 3, 8, 0, 9, 4, 6), intArrayOf(0, 0, 0, 0, 0, 0, 0, 0, 1), intArrayOf(11, 3, 5, 0, 11, 3, 5, 0, 1), intArrayOf(1, 0, 2, 0, 1, 0, 2, 0, 1), intArrayOf(16, 0, 10, 3, 8, 0, 10, 3, 8))
	)

0 representa grama.
1 representa uma seta para cima.
2 representa uma seta para baixo.
...
12 representa curva da esquerda para baixo.

onTouchEvent

O método que recebe e trata o toque do usuário também é 'seccionado/fatiado' pela variável time (tempo)

	override fun onTouchEvent(me: MotionEvent): Boolean {
		val x = me.x
		val y = me.y
		if (time == -1 && me.action == MotionEvent.ACTION_DOWN) {
			if (x < 250 * fractionScreenSize) //qdo o user aperta btn muda de fase
				stage = (stage + qttStages - 1) % qttStages
			else if (x > screenWidth - 250 * fractionScreenSize)
				stage = (stage + 1) % qttStages //qdo o user aperta btn proxima fase
			else { //inicializa variáveis para inicio do gameplay
				time = 0
				angle3 = startAngle[stage]
				carPositionX = (-stageStarts[stage][1] * 200 - 100) * fractionScreenSize
				carPositionY = (-stageStarts[stage][0] * 200 - 100) * fractionScreenSize
				cwPressed = false
				ccwPressed = false
				walkOut = false
			}
		}
		if (time > 0) {
			cwPressed = false
			ccwPressed = false
			if (x < 330 * fractionScreenSize && y > screenHeight - 380 * fractionScreenSize)
				ccwPressed = true
			if (x > screenWidth - 330 * fractionScreenSize && y > screenHeight - 380 * fractionScreenSize)
				cwPressed = true
			if (me.action == MotionEvent.ACTION_UP) {
				cwPressed = false
				ccwPressed = false
			}
			if (x > screenWidth - 130 * fractionScreenSize && y < 100 * fractionScreenSize)
				time = -1
		}
		if (time < -1 && me.action == MotionEvent.ACTION_DOWN) time = -1
		return true
	}

TimerTask

O onSizeChanged invoca/chama/starta um timer

Timer().schedule(gameloop, 0, 10)

As variáveis deltaDistance determinam o delocamento do carrinho no eixo x e no eixo y
São 36 valores
Cada valor é referente a um ângulo de inclinação.
deltaDistanceY[3] = 6 //porque quando o carro está 30 graus ele desloca 6 pixel no eixo y

	var gameloop: TimerTask = object : TimerTask() {
		val deltaDistanceX = intArrayOf(0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 7, 6, 5, 4, 3, 2, 1)
		val deltaDistanceY = intArrayOf(9, 8, 7, 6, 5, 4, 3, 2, 1, 0, -1, -2, -3, -4, -5, -6, -7, -8, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8)
		override fun run() {
			if (time >= 0) {
				time++
				if (ccwPressed) { //quando o botão de virar estiver pressinado
					angle3--
					if (angle3 == -3) angle3 = 105
				}
				if (cwPressed) {
					angle3++
					if (angle3 == 108) angle3 = 0
				}
				val upTile = intArrayOf(-1, -1) //variavel que armazena em qual pedaço da pista o carro está (x,y)
				if (-carPositionY / 200 / fractionScreenSize > 0 && -carPositionX / 200 / fractionScreenSize > 0 && -carPositionY / 200 / fractionScreenSize < course[stage].size && -carPositionX / 200 / fractionScreenSize < course[stage][0].size && course[stage][(-carPositionY / 200 / fractionScreenSize).toInt()][(-carPositionX / 200 / fractionScreenSize).toInt()] != 0) {
					upTile[0] = (-carPositionY / 200 / fractionScreenSize).toInt()
					upTile[1] = (-carPositionX / 200 / fractionScreenSize).toInt()
				}
				if (upTile[0] == stageEnds[stage][0] && upTile[1] == stageEnds[stage][1]) { //quando o carro está no fim da pista
					if (!walkOut && time < stageTimeLimit[stage]) {
						time = -4
					} else time = -2
				} else {
					// recalcula a posição do carro na pista
					var deltaPositionX = deltaDistanceX[angle3 / 3].toFloat()
					var deltaPositionY = deltaDistanceY[angle3 / 3].toFloat()
					if (upTile[0] == -1) {
						walkOut = true
						deltaPositionX /= 3f
						deltaPositionY /= 3f
					}
					if (time > 100) carPositionX += fractionScreenSize * deltaPositionX * maxSpeed[stage] * 0.0642f
					if (time > 100) carPositionY += fractionScreenSize * deltaPositionY * maxSpeed[stage] * 0.0642f
				}
			}
			invalidate()
		}
	}

invalidate é um método da classe view que invoca o onDraw

Código completo

Clique aqui e conheça o tutorial completo (gratuito open-source)

Arquivos do Projeto

Projeto Square Motor
Projeto Fluid Fuel

Jogos Prontos

Square Motor
Fluid Fuel

Carregando publicação patrocinada...
0
1
1