Phần 2. Xây dựng màn hình Home của Game 2D

0
Dịch vụ dạy kèm gia sư lập trình
Bài này thuộc phần 2 của 3 phần trong series Phát triển Game 2D bằng React Native

Phần tiếp theo trong series xây dựng game 2D bằng React Native, chúng ta sẽ cùng nhau bắt tay vào xây dựng màn hình Home.

Mình khuyên bạn nên đọc lại phần 1 để biết React Native là gì và chuẩn bị môi trường phát triển trước khi bắt tay vào phần 2 này.

Cấu trúc lại mã nguồn màn hình Home

Để mã nguồn được clean hơn, mình sẽ tách phần mã JS và CSS ra làm 2 file riêng biệt: index.js và styles.js

Cấu trúc mã nguồn cho mỗi màn hình sẽ đại khái như sau:

# the screens’ directory structure as of now
screens
├── Home
│   ├── index.js
│   └── styles.js
└── Routes.js

OK, đầu tiên, chúng ta mở style.js để thêm style cho containner:

import { StyleSheet } from "react-native";

export default StyleSheet.create({
 container: {
   flex: 1,
   backgroundColor: "#0a0a0a",
   justifyContent: "center",
   alignItems: "center"
 }
});

Tiếp theo là import cái style nào vào index.js (Nhớ là phải xóa đoạn code style cũ đi nhé)

Giờ code của index.js sẽ như sau:

// basic imports ...
import styles from './styles';

export default class Home extends Component {
 render() {
   return (
     <View style={styles.container}>
    {/* // this View is empty on purpose! */}
     </View>
   );
 }
}

💦 Đọc thêm:

Tạo Header

Cũng giống như mọi ứng dụng khác thôi, chúng ta sẽ cần một Header hiển thị ở mọi màn hình. Với game 2D này, chúng ta sẽ hiển thị một logo như dưới đây:

react-native-mobile-app-name

Để có thể tái sử dụng mã nguồn, chúng ta sẽ tạo một component và đặt tên là Header. Cách làm như sau, bạn tạo mới một file js: Header.js trong thư mục components và copy đoạn code sau:

import React from "react";
import { Text, View, StyleSheet } from "react-native";

const Header = () => (
 <View style={{ flexDirection: "row" }}>
   <Text style={[styles.header, { color: "#E64C3C" }]}>c</Text>
   <Text style={[styles.header, { color: "#E57E31" }]}>o</Text>
   <Text style={[styles.header, { color: "#F1C431" }]}>l</Text>
   <Text style={[styles.header, { color: "#68CC73" }]}>o</Text>
   <Text style={[styles.header, { color: "#3998DB" }]}>r</Text>
   <Text style={styles.header}>blinder</Text>
 </View>
);

const styles = StyleSheet.create({
 header: {
   fontSize: 50,
   color: "#ecf0f1",
   fontFamily: "dogbyte"
 }
});

export { Header };

Tiếp theo thì chúng ta export nó vào trong index.js

export * from './Header'

Để sử dụng thì chúng ta cần import nó ở màn hình nào cần tới. Ví dụ ở đây là màn hình Home chẳng hạn:

import { Header } from '../../components'
// …
<View style={styles.container}>
       <Header />
     	</View>

Giờ thì chạy code và kiểm tra thành quả

react-native-mobile-app-homescreen-only-with-text

Nhìn cũng khá ổn rồi đúng không?! Tuy nhiên, nếu tinh mắt thì bạn sẽ phát hiện ra một lỗi nhỏ, và lỗi này chỉ bị trên iOS, đó là thành status bar bị trùng màu với ảnh nền (màu đen), thành ra các chỉ số như đồng hồ, cột sóng bị mất.

Chúng ta sẽ sửa lỗi này trước khi bắt tay vào bước tiếp theo nhé.

Sửa lỗi Status Bar

Chúng ta mở App.js và import component StatusBar  từ React-native và Fragment  từ React.

Giải thích thêm một chút: Fragment là một DOM ảo, nó không xuất hiện trong DOM thật, được sử dụng khi bạn muốn gom nhiều element vào một nhóm mà không muốn thêm thẻ div (mục đích để tránh việc lồng element quá nhiều).
import React, { Component, Fragment } from 'react';
import { StatusBar } from 'react-native';

Sau đó thì gọi nó ra:

else {
   return (
      <Fragment>
        <StatusBar barStyle="light-content" />
        <Routes />
      </Fragment>
    )
}

Giờ thì tận hưởng thành quả thôi:

react-native-mobile-app-homescreen-with-status-bar

Chỉ với một dòng code nhưng mà trải nghiệm UX tốt hơn hẳn đúng không!

Thêm các thành phần tương tác khác

Như design UX ban đầu của màn hình Home, chúng ta cần một nút Play lớn ở giữa màn hình.

react-native-mobile-app-play-button

Chúng ta sẽ không sử dụng Button component mặc định của react native ( vì no chứa ảnh và chúng ta cũng không cần background hay border). Thay vào đó, chúng ta sử dụng TouchableOpacity

Đây là component cho phép chúng ta bọc bất kỳ component nào khác bên trong và biến nó trở thành một đối tượng có thể nhận sự kiện touch từ người dùng.

Chúng ta import nó thôi:

import { View, Text, Image, TouchableOpacity } from "react-native";

Tạo sẵn một callback function và hiện tại nó chỉ đơn giản là hiển thị console.log ra thôi:

onPlayPress = () => {
  console.log("onPlayPress event handler");
};

Sau đó là thêm callback function này vào TouchableOpacity:

<TouchableOpacity onPress={this.onPlayPress} style={{ flexDirection: 'row', alignItems: 'center' }}>
  <Image
    source={require("../../assets/icons/play_arrow.png")}
    style={styles.playIcon}
  />
  <Text style={styles.play}>PLAY!</Text>
</TouchableOpacity>
Lưu ý: Để trỏ tới một ảnh trong dự án, chúng ta sử dụng hàm require(…). Đây là cách dễ nhất rồi, ngoài ra thì cũng có một số cách khác nữa tốt hơn, bạn có thể tự tìm hiểu sau nhé.

Tiếp theo là thêm styles trong style.js

play: {
   fontSize: 45,
   fontFamily: "dogbyte",
   color: "#ecf0f1",
   marginTop: 5
 },
 playIcon: {
   height: 60,
   width: 60,
   marginRight: 15
 }

Ok, giờ thì chạy ứng dụng và kiểm tra thôi

react-native-mobile-app-logo-and-start

Phần tiếp theo của bài viết, chúng ta sẽ tạo một component hiển thị high score (điểm cao)

Tạo High Score component

Với component này , chúng ta làm tương tự như lúc trước tạo nút play vậy, điểm khác biệt là với component này, chúng ta không phải xử lý sự kiện touch, chỉ đơn giản là hiển thị dữ liệu điểm ra màn hình.

Quay trở lại styles.js và thêm đoạn style sau:

hiscore: {
  fontSize: 28.5,
  fontFamily: "dogbyte",
  color: "#ecf0f1",
  marginTop: 5
},
trophyIcon: {
  height: 45,
  width: 45,
  marginRight: 12.5
}

Sau đó thì thêm <Image><Text> vào trong một <View>

<View style={{ flexDirection: 'row', alignItems: 'center' }}>
  <Image
    source={require("../../assets/icons/trophy.png")}
    style={styles.trophyIcon}
  />
  <Text style={styles.hiscore}>Hi-score: 0</Text>
</View>

Hiện tại chúng ta chỉ hard code vậy thôi, sau này khi làm xử lý logic thì chúng ta sẽ hiển thị giá trị điểm thật lên.

Tạo Leaderboard Button

Tương tự như hai phần trên, mình sẽ không hướng dẫn chi tiết cụ thể ở đây, bạn tự thực hành nhé, coi như là một bài tập

react-native-mobile-app-highscore-leaderboard

Thêm copyright banner & speaker icon

Trong bản design UX ban đầu, chúng ta có một vài thứ nữa ở phía cuối màn hình như: icon loa để bật tắt âm thanh của game, hay là dòng chữ bản quyền.

react-native-mobile-app-copyrights

Cả hai mục này sẽ cố định ở phía cuối của màn hình nên chúng ta sẽ đưa hết chúng vào một containner

Styles:

bottomContainer: {
   position: "absolute",
   left: 15,
   right: 15,
   bottom: 12.5 // the 2.5px bottom margin from the text is subtracted from the 15px spacing
 },
 copyrightText: {
   fontSize: 16,
   fontFamily: "dogbyte",
   marginBottom: 2.5
 }

Và trong Js:

<View style={styles.bottomContainer}>
  <Text style={[styles.copyrightText, { color: "#E64C3C" }]}>
    Music: Komiku
  </Text>
  <Text style={[styles.copyrightText, { color: "#F1C431" }]}>
    SFX: SubspaceAudio
  </Text>
  <Text style={[styles.copyrightText, { color: "#3998DB" }]}>
    Development: RisingStack
  </Text>
</View>

Việc chuyển đổi biểu tượng loa chỉ đơn giản là thay đổi trạng thái state mà thôi, mỗi một giá trị state, mình gắn vào một biểu tượng (còn việc thêm nhạc và hiệu ứng sfx thì mình sẽ thực hiện ở phần sau, ở bài viết sau).

Định nghĩa state cho biểu tượng loa:

state = {
  isSoundOn: true
};

Mỗi lần touch vào biểu tượng loa, chúng ta sẽ chuyển trạng thái tương ứng với hai biếu tượng như dưới đây:

react-native-mobile-app-music-toggle

Trong hàm render, chúng ta chuyển trạng thái như sau:

render() {
   const imageSource = this.state.isSoundOn
     ? require("../../assets/icons/speaker-on.png")
     : require("../../assets/icons/speaker-off.png");
   // ...

Và phần hiển thị biểu tượng loa ở phía cuối, bên phải màn hình:

<View style={{ flex: 1 }} />
<TouchableOpacity onPress={this.onToggleSound}>
    <Image source={imageSource} style={styles.soundIcon} />
</TouchableOpacity>

react-native-mobile-app-styling

Giờ bạn chạy và kiểm tra thành quả nhé.

Chúng ta tạm kết thúc phần 2 của series tạo game 2D bằng React native tại đây, bạn có thể tải mã nguồn của bài viết về để tham khảo thêm.

Hẹn gặp lại các bạn ở phần tiếp theo: Viết code logic cho game.

Xem tiếp các bài trong Series
Phần trước: [Series] Tạo Game 2D hoàn chỉnh bằng React NativePhần kế tiếp: Phần 3. Xây dựng Logic chính màn hình Game 2D
Dịch vụ phát triển ứng dụng mobile giá rẻ - chất lượng
Bài trước13 phím tắt trong VS Code vô cùng hữu ích
Bài tiếp theoPhần 3. Xây dựng Logic chính màn hình Game 2D
Sơn Dương
Tên đầy đủ là Dương Anh Sơn. Tốt nghiệp ĐH Bách Khoa Hà Nội. Mình bắt đầu nghiệp coder khi mà ra trường chẳng xin được việc đúng chuyên ngành. Mình tin rằng chỉ có chia sẻ kiến thức mới là cách học tập nhanh nhất. Các bạn góp ý bài viết của mình bằng cách comment bên dưới nhé !

Bình luận. Cùng nhau thảo luận nhé!

avatar
  Theo dõi bình luận  
Thông báo