Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Proyecto Final
Proyecto Final
DOMINGO SAVIO
PROYECTO FINAL
“APLICACION ANDROID CON REACT
NATIVE”
(Arcade Classics)
a. INTRODUCCION
La siguiente aplicación ha sido desarrollada utilizando React Native para la
plataforma de Android, enfocada en el entretenimiento y apoyada de
diversas tecnologías de programación como JavaScript otorga una interfaz
intuitiva y fácil de manejar con un menú en la parte inferior donde
podremos seleccionar entre tres diferentes juegos del arcade clásico
intentando capturar el interés del usuario y brindarle una experiencia de
entretenimiento única en la palma de la mano.
b. MARCO TEORICO
Arcade Classics ha sido desarrollado utilizando la tecnología de REACT-
NATIVE, este un framework de programación de aplicaciones nativas
multiplataforma que está basado en JavaScript y ReactJS.
Creado en Facebook y publicado en Febrero de 2015 React Native es un
framework para la creación de aplicaciones nativas para iOS y Android.
Para programar aplicaciones usando React Native usamos como lenguaje
de programación JavaScript, en lugar de Swift, JAVA u otros lenguajes y
por el resto usamos React para la creación de la interfaz del usuario.
pág. 2
A diferencia de otras soluciones como Ionic, Cordova o Phonegap, React
Native no utiliza WebViews ni produce HTML o CSS, es decir, no usa
tecnologías web para la interfaz de tu aplicación. La pregunta es, si no usa
estas tecnologías cómo es que podemos usar React y JavaScript para la
creación de aplicaciones nativas.
Saber que las aplicaciones de React Native no son 100% nativas, es el
primer paso para entender qué está sucediendo internamente para que una
app escrita en JavaScript se ejecute de manera “nativa” entre comillas.
Las aplicaciones de React Native se componen de 3 elementos
principalmente:
1. Código nativo: Un proyecto de React Native puede contener código
nativo en combinación con el código de JavaScript, es decir, si estás
desarrollando una app para Android podrías tener código de JAVA
aparte del proyecto de JavaScript.
2. JavaScriptCore VM: Las aplicaciones de ReactNative utilizan
JavaScriptCore, el motor de ejecución de JavaScript de Safari para
ejecutar el código de JavaScript de nuestra app, esto significa que el
código que escribas de JavaScript no será compilado, será ejecutado
como JavaScript dentro de tu app, por eso decimos que no es 100%
nativo, porque internamente hay una máquina virtual que ejecuta el
código de JavaScript.
3. React Native Bridge: Como su nombre lo dice, el React Native
Bridge es un puente que se encarga de comunicar el código de la
máquina virtual de JavaScript con el código nativo y las APIs nativas
de la plataforma en la que se ejecuta nuestra aplicación.
Quizás hayas escuchado de un concepto muy importante de las
aplicaciones de React, el Virtual DOM. El Virtual DOM es una
representación virtual, como su nombre indica, de cómo se deben mostrar
los componentes en la interfaz, de cómo se hará el render.
En los navegadores web, el virtual DOM se representa a través del
Document Object Model con elementos de HTML, la ventaja de que el
Virtual DOM sea una capa intermedia entre el código y la representación
final de nuestra app permite que modifiquemos dicha representación
basado en la descripción del virtual DOM, piensa en el Virtual DOM como
una especificación textual de cómo debe verse la app, digamos, habrá un
text input aquí, un botón de color rojo allá, etc. Después esta especificación
pág. 3
debe trasladarse a los componentes de la plataforma donde se verá, de
nuevo, en el caso de una página web esto significa elementos de HTML.
pág. 4
Con esto ya tendremos nuestro proyecto creado de React Native
Podemos ejecutarlo enseguida con el comando
Y luego lo importamos
Matter.js
pág. 5
Procedemos a instalar el paquete matter.js
Y luego lo importamos
ELABORACION DE LA INTERFAZ
Primero incorporamos un bottom navigator para que sea nuestro menú de la
aplicación. Para esto fue necesario elaborar un componente llamado
MainContainer, el cual llama a diferentes clases de acuerdo con su menú.
// Screens
import GameOne from './screens/GameOne';
import GameTwo from './screens/GameTwo';
import GameThree from './screens/GameThree';
import GameFour from './screens/GameFour';
//Screen names
const gameOne = "Memory";
const gameTwo = "Snake";
const gameThree = "Flappy";
const gameFour = "About Us"
function MainContainer() {
return (
<NavigationContainer>
<Tab.Navigator
initialRouteName={gameOne}
screenOptions={({ route }) => ({
pág. 6
tabBarIcon: ({ focused, color, size }) => {
let iconName;
let rn = route.name;
elevation: 24,
}
}}>
pág. 7
<Tab.Screen name={gameOne} component={GameOne} />
<Tab.Screen name={gameTwo} component={GameTwo} />
<Tab.Screen name={gameThree} component={GameThree} />
<Tab.Screen name={gameFour} component={GameFour} />
</Tab.Navigator>
<StatusBar style="auto" />
</NavigationContainer>
);
}
function App() {
return (
<MainContainer/>
);
}
pág. 8
CRAZY CARDS
const cards = [
"🍇",
"🍉",
"🍏",
"🍊",
"🍍",
"🍌",
];
pág. 9
const timeOutId = setTimeout(() => setSelectedCards([]), 500);
return () => clearTimeout(timeOutId);
}
}, [selectedCards])
return (
<ImageBackground source={localImagen} style={styles.container}>
<View style={styles.title2}>{didPlayerWin() ?
<Image source={winner}
style={{
resizeMode:'cover',
height: 150,
width: 350,
}}
/>
:
<Image source={title}
style={{
resizeMode:'cover',
height: 150,
width: 350,
}}
/>
}
</View>
<Text style={styles.title}>Score : {score}</Text>
pág. 10
<View style={styles.board}>
{board.map((card, index) => {
const isTurnedOver = selectedCards.includes(index) || matchedCards.includes(index);
return(
<Card
key={index}
isTurnedOver={isTurnedOver}
onPress={() => handleTapCard(index)}
>{card}</Card>
)
})}
</View>
{didPlayerWin() &&
<TouchableOpacity style={{ backgroundColor: 'tomato', paddingHorizontal: 30,
paddingVertical: 10, borderRadius: 100/2, position: 'relative', bottom: 70}}
onPress={resetGame}>
<Text style={{ fontWeight: 'bold', color: 'white', fontSize: 30 }}>
Jugar De Nuevo
</Text>
</TouchableOpacity>
}
<StatusBar hidden={true} />
</ImageBackground>
);
}
pág. 11
},
title2: {
position: 'relative',
bottom: 40,
justifyContent: 'center',
alignItems: 'center',
},
titleBtn: {
padding: 25,
paddingBottom: 4,
backgroundColor:'tomato',
borderRadius:50/2,
fontWeight: 'bold',
margin: 15,
position: 'relative',
bottom: 60,
},
board: {
flexDirection: 'row',
flexWrap: 'wrap',
justifyContent: 'center',
position: 'relative',
bottom: 80,
}
});
function shuffle(array) {
for (let i = array. length - 1; i > 0; i--){
const randomIndex = Math.floor(Math.random() * (i + 1));
//Intercambia los elementos en i y randomIndex
[array[i], array[randomIndex]] = [array[randomIndex], array[i]];
}
return array;
}
pág. 12
Para evitar la sobrecarga de código se utilizó un componente externo
llamado card.js que es únicamente una tarjeta props con parámetros
modificables.
import * as React from 'react';
import { Pressable, Text, StyleSheet } from 'react-native';
elevation: 24,
},
cardDown: {
width: 100,
pág. 13
height: 100,
margin: 10,
borderWidth: 10,
borderColor:'tomato',
backgroundColor:'#FFD2BB',
alignItems: 'center',
justifyContent: 'center',
borderRadius:50/2,
shadowColor: "gray",
shadowOffset: {
width: 0,
height: 12,
},
shadowOpacity: 0.58,
shadowRadius: 16.00,
elevation: 24,
},
text: {
fontSize: 46,
color: 'black',
}
})
pág. 14
En su programación encontramos el código principal que encapsula las
diferentes clases que se mencionaran a continuación.
import React, { Component } from "react";
import { AppRegistry, StyleSheet,ImageBackground, StatusBar, SafeAreaView, View,
Alert, Text, Button, TouchableOpacity } from "react-native";
import { GameEngine, dispatch } from "react-native-game-engine";
import { Head } from "../components/head";
import { Food } from "../components/food";
import { Tail } from "../components/tail";
import { GameLoop } from "../components/systems";
import Constants from '../components/Constants';
reset = () => {
this.engine.swap({
head: { position: [0, 0], xspeed: 1, yspeed: 0, nextMove: 10, updateFrequency: 10,
size: 20, renderer: <Head />},
food: { position: [this.randomBetween(0, Constants.GRID_SIZE - 1),
this.randomBetween(0, Constants.GRID_SIZE - 1)], size: 20, renderer: <Food />},
tail: { size: 20, elements: [], renderer: <Tail /> }
});
this.setState({
running: true
pág. 15
});
}
render() {
const localImagen = require('../img/game2.jpg')
return (
<ImageBackground source={localImagen} style={styles.container}>
<View>
<TouchableOpacity style={{ backgroundColor: 'tomato', paddingHorizontal: 30,
paddingVertical: 10, borderRadius: 100/2, position: 'relative', bottom: 10}}
onPress={this.reset
}>
<Text style={{ fontWeight: 'bold', color: 'white', fontSize: 30 }}>
Reiniciar 🐍
</Text>
</TouchableOpacity>
</View>
<GameEngine
ref={(ref) => { this.engine = ref; }}
style={[{ width: this.boardSize, height: this.boardSize, backgroundColor:
'#ffffff', flex: null, borderColor: 'black', borderWidth: 2, marginBottom: 20}]}
systems={[ GameLoop ]}
entities={{
head: { position: [0, 0], xspeed: 1, yspeed: 0, nextMove: 10,
updateFrequency: 10, size: 20, renderer: <Head />},
food: { position: [this.randomBetween(0, Constants.GRID_SIZE - 1),
this.randomBetween(0, Constants.GRID_SIZE - 1)], size: 20, renderer: <Food />},
tail: { size: 20, elements: [], renderer: <Tail /> }
}}
running={this.state.running}
onEvent={this.onEvent}>
</GameEngine>
<View style={styles.controls}>
<View style={styles.controlRow}>
<TouchableOpacity onPress={() => { this.engine.dispatch({ type: "move-
up" })} }>
<View style={styles.control}>
<Text style={{fontWeight: 'bold', fontSize: 80}}>⬆️</Text>
</View>
pág. 16
</TouchableOpacity>
</View>
<View style={styles.controlRow}>
<TouchableOpacity onPress={() => { this.engine.dispatch({ type: "move-
left" })} }>
<View style={styles.control}>
<Text style={{fontWeight: 'bold', fontSize: 80}}>⬅️</Text>
</View>
</TouchableOpacity>
<View style={[styles.control, { backgroundColor: null}]} />
<TouchableOpacity onPress={() => { this.engine.dispatch({ type: "move-
right" })}}>
<View style={styles.control}>
<Text style={{fontWeight: 'bold', fontSize: 80}}>➡️</Text>
</View>
</TouchableOpacity>
</View>
<View style={styles.controlRow}>
<TouchableOpacity onPress={() => { this.engine.dispatch({ type: "move-
down" })} }>
<View style={styles.control}>
<Text style={{fontWeight: 'bold', fontSize: 80}}>⬇️</Text>
</View>
</TouchableOpacity>
</View>
</View>
<View>
<Text>jas</Text>
</View>
</ImageBackground>
);
}
}
pág. 17
height: 300,
flexDirection: 'column',
marginBottom: 40,
position: 'relative',
bottom: 20,
},
controlRow: {
height: 100,
width: 300,
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row'
},
control: {
width: 100,
height: 100,
alignItems: 'center',
justifyContent: 'center',
},
titleBtn: {
paddingHorizontal: 25,
marginBottom: 5,
borderWidth: 5,
backgroundColor:'tomato',
borderColor:'white',
borderRadius:50/2,
position: 'relative',
bottom: 10,
},
});
pág. 18
Código de Clase Systems donde podremos configurar las variables de
juego , velocidad, tamaño y demás.
import React, { Component } from "react";
import Constants from './Constants';
if (events.length){
for(let i=0; i<events.length; i++){
if (events[i].type === "move-down" && head.yspeed != -1){
head.yspeed = 1;
head.xspeed = 0;
} else if (events[i].type === "move-up" && head.yspeed != 1){
head.yspeed = -1;
head.xspeed = 0;
} else if (events[i].type === "move-left" && head.xspeed != 1){
head.yspeed = 0;
head.xspeed = -1;
} else if (events[i].type === "move-right" && head.xspeed != -1){
head.yspeed = 0;
head.xspeed = 1;
}
}
}
head.nextMove -= 1;
if (head.nextMove === 0){
head.nextMove = head.updateFrequency;
if (
head.position[0] + head.xspeed < 0 ||
head.position[0] + head.xspeed >= Constants.GRID_SIZE ||
head.position[1] + head.yspeed < 0 ||
head.position[1] + head.yspeed >= Constants.GRID_SIZE
){
// snake hits the wall
dispatch({ type: "game-over" })
} else {
pág. 19
// move the tail
let newTail = [[head.position[0], head.position[1]]];
tail.elements = newTail.concat(tail.elements).slice(0, -1);
// snake moves
head.position[0] += head.xspeed;
head.position[1] += head.yspeed;
return entities;
};
export { GameLoop };
ahora los componentes principales del juego SNAKE, entre ellos la comida
FOOD
import React, { Component } from "react";
import { StyleSheet, View } from "react-native";
render() {
const x = this.props.position[0];
pág. 20
const y = this.props.position[1];
return (
<View style={[styles.finger, { width: this.props.size, height: this.props.size, left: x *
this.props.size, top: y * this.props.size }]} />
);
}
}
export { Food };
render() {
const x = this.props.position[0];
const y = this.props.position[1];
return (
<View style={[styles.finger, { width: this.props.size, height: this.props.size, left: x *
this.props.size, top: y * this.props.size }]} />
);
}
}
pág. 21
export { Head };
render() {
return (
<View style={{ width: Constants.GRID_SIZE * this.props.size, height:
Constants.GRID_SIZE * this.props.size }}>
{tailList}
</View>
);
}
}
export { Tail };
FLAPPY SQUARE
pág. 22
Este es un videojuego que utiliza fisicas mas avanzadas como el uso de
gravedad y componentes en 2D, con la finalidad de poder brindar una
experiencia de usuario de un pájaro que vuela a través de obstáculos
generados de manera automática.
pág. 23
style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 }}
>
<StatusBar style="auto" hidden={true} />
</GameEngine>
{!running ?
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<TouchableOpacity style={{ backgroundColor: 'tomato', paddingHorizontal: 30,
paddingVertical: 10, borderRadius: 100/2 }}
onPress={() => {
setCurrentPoints(0)
setRunning(true)
gameEngine.swap(entities())
}}>
<Text style={{ fontWeight: 'bold', color: 'white', fontSize: 30 }}>
INICIAR 🐥
</Text>
</TouchableOpacity>
</View> : null}
<StatusBar hidden={true} />
</ImageBackground>
);
}
pág. 24
const windowWidth = Dimensions.get('window').width
world.gravity.y = 0.4;
Siendo la clase index.js la clase padre del juego Flappy Square, esta se
encarga de poner en escena los diferentes componentes, entre ellos la clase
physics.js esta clase utiliza la librería matter.js para la simulación de la
gravedad y la interacción de los objetos en escena dicho de otra forma las
colisiones, velocidad y movimientos en escena.
import Matter from "matter-js";
import { getPipeSizePosPair } from "./navigation/utils/random";
pág. 25
const windowWidth = Dimensions.get('window').width
Matter.Engine.update(engine, time.delta)
}
if (entities[`ObstacleTop${index}`].body.bounds.max.x <= 0) {
const pipeSizePos = getPipeSizePosPair(windowWidth * 0.9);
Matter.Body.setPosition(entities[`ObstacleTop${index}`].body,
pipeSizePos.pipeTop.pos)
Matter.Body.setPosition(entities[`ObstacleBottom${index}`].body,
pipeSizePos.pipeBottom.pos)
entities[`ObstacleTop${index}`].point = false
}
Matter.Body.translate(entities[`ObstacleTop${index}`].body, { x: -3, y: 0 })
Matter.Body.translate(entities[`ObstacleBottom${index}`].body, { x: -3, y: 0 })
}
return(
<View style={{
borderWidth: 2,
borderColor: color,
borderStyle: 'solid',
backgroundColor: 'yellow',
position: 'absolute',
left: xBody,
top: yBody,
width: widthBody,
height: heightBody
}}/>
)
}
return {
body: initialBird,
pág. 27
color,
pos,
renderer: <Bird/>
}
}
return (
<View style={{
borderWidth: 2,
borderColor: color,
borderStyle: 'solid',
position: 'absolute',
backgroundColor:'#3AFF00',
left: xBody,
top: yBody,
width: widthBody,
height: heightBody
}} />
)
}
pág. 28
label,
isStatic: true
}
)
Matter.World.add(world, initialObstacle)
return {
body: initialObstacle,
color,
pos,
renderer: <Obstacle />
}
}
return(
<View style={{
backgroundColor: color,
position: 'absolute',
left: xBody,
top: yBody,
width: widthBody,
height: heightBody
}}/>
)
}
pág. 29
export default (world, color, pos, size) => {
const initialFloor = Matter.Bodies.rectangle(
pos.x,
pos.y,
size.width,
size.height,
{
label: 'Floor',
isStatic: true
}
)
Matter.World.add(world, initialFloor)
return {
body: initialFloor,
color,
pos,
renderer: <Floor/>
}
}
return (
<ImageBackground source={localImagen} style={{ flex: 1, alignItems: 'center',
justifyContent: 'center' }}>
<Image source={topText}
style={{
resizeMode:'cover',
height: 100,
width: 350,
}}
/>
<Image source={tapiaCode}
style={{
resizeMode:'cover',
height: 250,
width: 250,
borderRadius: 300/2,
marginBottom: 20
}}
/>
<Image source={bottomText}
style={{
resizeMode:'cover',
pág. 31
height: 100,
width: 350,
}}
/>
<Image source={arcade}
style={{
resizeMode:'cover',
height: 100,
width: 250,
marginTop: 10
}}
/>
<StatusBar hidden={true} />
</ImageBackground>
);
}
Comentarios Finales
Para el desarrollo de este proyecto se empleo la siguiente paqueteria de
react native , además de usar como gestor de paquetes predeterminados la
SDK 42 de expo y una version anterior de react native, por motivos de
estabilidad.
"dependencies": {
"@react-native-community/masked-view": "0.1.10",
"@react-navigation/bottom-tabs": "^5.11.11",
"@react-navigation/native": "^5.9.4",
"@react-navigation/stack": "^5.14.5",
"expo": "~42.0.0",
"expo-font": "~9.2.1",
"expo-status-bar": "~1.0.4",
"expo-updates": "~0.8.2",
"matter-js": "^0.18.0",
"react": "16.13.1",
"react-dom": "16.13.1",
"react-native": "https://github.com/expo/react-native/archive/sdk-42.0.0.tar.gz",
"react-native-game-engine": "^1.2.0",
"react-native-gesture-handler": "~1.10.2",
"react-native-reanimated": "~2.2.0",
"react-native-safe-area-context": "3.2.0",
"react-native-screens": "~3.4.0",
"react-native-web": "~0.13.12"
pág. 32
},
"devDependencies": {
"@babel/core": "^7.9.0"
},
"private": true
}
pág. 33
"icon": "./assets/icon.png",
"splash": {
"image": "./assets/splash.png",
"resizeMode": "contain",
"backgroundColor": "#ffffff"
},
"updates": {
"fallbackToCacheTimeout": 0
},
"assetBundlePatterns": [
"**/*"
],
"ios": {
"supportsTablet": true
},
"android": {
"package": "com.tapiacode.arcadeclassic",
"versionCode": 1
},
"web": {
"favicon": "./assets/favicon.png"
}
}
}
pág. 34
Para finalizar podemos ingresar a la web de expo con el siguiente enlace
https://expo.dev/accounts/tapiacode/projects/arcadeClassic/builds/
832fb046-20ce-446d-a64d-522f9051ae52
donde tenemos alojado el apk-release de nuestro proyecto compilado como
se observa a continuacion.
CONCLUSION
Para finalizar destacar que el uso de react native para desarrollo de
aplicaciones móviles hibridas resulto muy practico y una experiencia
entretenida que vale la pena destacar, por la gran facilidad con la gestión de
sus paquetes y la posibilidad de ser multiplataforma y además de generar
las llaves y compilación de manera rápida y eficiente para ambas
plataformas, quizá el manejo de sus componentes es algo tedioso cuando
comenzamos pero podemos acostumbrarnos con la practica y con la gran
cantidad de documentación en la web.
d. BIBLIOGRAFIA
pág. 35
Documentación en la web:
https://reactnative.dev/docs/environment-setup
https://www.npmjs.com/package/react-native-game-
engine#introduction
https://www.npmjs.com/package/matter-js#features
https://blog.logrocket.com/react-native-game-development-tutorial/
https://ethercreative.github.io/react-native-shadow-generator/
https://codewithbeto.dev/projects
https://codewithbeto.dev/projects/memory-game
Documentación en YouTube:
https://www.youtube.com/@betomoedano
https://www.youtube.com/@AniaKubow
https://www.youtube.com/@SimpleCoder
https://www.youtube.com/watch?v=zK2xYD4Nytw&t=393s
https://www.youtube.com/watch?v=0gFt7W3bhTY
https://www.youtube.com/watch?v=IfRd9OWQAxw&t=40s
e. ANEXOS
English words appended
# WORD PRONUNCIATION TRANSLATION
1 Modified madufaid modificado
2 Because bɪˈkəz porque
3 Building bil′ding construccion
4 Standalone stan·duh·lown autónomo
5 Complexity kəm plek′si tē complejidad
6 Becoming bikamin convirtiendose
7 Handler hand′lər manipulador
8 Highlight hī′līt Destacar
9 Expecting spɛktɪŋ Esperando
10 Turnover ˈtɝnˌoʊvɚ envuelto
11 wrapped ræpt envuelto
12 should shŏŏd debería
13 engine enyaine Motor
14 making meykin Construyendo
15 introduction introducshion introduccion
16 getting gerin consiguiendo
17 meaning minin sentid
pág. 36
18 dependency dapendenci Dependecy
19 package Pak-eish Paquete
20 command Coumand dominio
21 styling stayling estilismo
22 hook jok gancho
23 render wrender prestar
24 entities entitis entidades
25 board bord tablero
26 grow grou Crecer
27 randomness randones aletoriedad
28 proactively proactivly proactivamente
29 framework freimwork Estructura
30 happen japan morder
pág. 37