한이음 프로젝트로 앱 개발을 거의 마무리하고 사용자 테스트를 진행하고 있는데

오늘 회의에서 얘기한 결과, 폰트의 가독성에 대한 피드백이 많이 있어서 폰트를 수정했다.

 

나눔스퀘어체로 수정했는데 글자 자체는 깔끔하지만 줄간격이 너무 좁아서 빼곡한 느낌이 있었다.

 

 

앱에서 제공하는 진단테스트, 상담 채팅 기능에서는 한 화면에 많은 양의 텍스트가 보이는데

이렇게 글자가 빼곡하면 가독성이 저하된다고 판단해서 줄간격을 넓히는 방법을 찾아보았다.

 

방법은 android:lineSpacingExtra 속성을 사용하는 것이었다.

 

<TextView
...
android:lineSpacingExtra="5dp"
/>

 

이런 식으로 특정 TextView에 직접 속성을 지정해줘도 되고

나는 앱 전체 텍스트에 적용이 되도록 style.xml에 TextView의 스타일을 지정했다.

 

values > style.xml

  <style name="TextViewFontStyle" parent="@android:style/Widget.DeviceDefault.TextView">
      <item name="android:fontFamily">@font/custom_font_family</item>
      <item name="android:textSize">16sp</item>
      <item name="android:lineSpacingExtra">5sp</item>
  </style>

 

참고로 custom_font_family는 폰트자체 속성을 지정하기 위해 작성한 것으로, 코드는 다음과 같다.

 

font > custom_font.family.xml

<?xml version="1.0" encoding="utf-8"?>
<font-family xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <font
        android:font="@font/nanumsquare"
        android:fontStyle="normal"
        android:fontWeight="500"
        app:font="@font/nanumsquare"
        app:fontStyle="normal"
        app:fontWeight="500"
        tools:ignore="ResourceCycle" />

</font-family>

 

font 폴더에 있는 나눔스퀘어 ttf를 지정했다.

 


 

 

줄간격 수정 전후를 비교해보았을 때 가독성이 훨씬 좋아졌다.

안드로이드 스튜디오 앱 개발 프로젝트를 진행하면서 다크모드는 생각도 못하고 있었는데

다크모드를 적용하고 앱을 실행하면 레이아웃의 많은 부분이 원치 않게 깨지는 현상을 발견했다.

 

res - values - themes 폴더에 일반/다크모드 전용 themes.xml 파일이 있어서

원하는 사람은 themes.xml (night) 파일을 수정하여 다크모드에 맞게 따로 디자인을 적용해도 좋을 것 같으나

나는 다크모드를 지원하지 않는 방법을 찾아보았다.

 

 

앱을 실행할 때 가장 먼저 실행되는 액티비티인 로딩 화면(스플래시 화면) java 파일 onCreate 메소드에

다음과 같이 코드를 작성했다.

public class ActivitySplash extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO); //다크모드 지원 X
	//생략
    }
}

 

이렇게 작성하면 AppCompatActivity를 상속한 클래스는 다크모드를 킨 상태로 앱을 이용하더라도

다크모드 테마가 아닌 기본 테마가 적용이 된다.

 

하지만 일부 페이지는 위의 코드를 작성해도 다크모드가 적용되는 것을 발견했는데

AppCompatActivity가 아닌 Activity를 상속한 클래스들이었다.

따라서 해당 클래스들을 모두 AppCompatAcitivty를 상속하도록 수정했다.

 

이로써 다크모드 적용으로 인해 앱의 디자인이 원치 않게 깨지는 현상을 막을 수 있었다!

Dialogflow를 사이트 자체에서 세팅하는 것도 어려운 일이었는데

이것을 앱 화면에 띄우기 위해 Android Studio 내에서 코드를 짜는 것도 역시 어려운 일이었다.

관련 라이브러리가 무엇이 있는지도 모르고 어떤 메소드를 사용해야하고,

어떤 로직을 짜야하는지 너무 막막했기 때문이다.

 

그런 와중에 발견한 한줄기의 빛...!!!!🌟

 

https://youtu.be/zVxDBBCdpfY

 

영상으로 방법도 순서대로 알려주었으며

더보기란에 해당 코드의 GitHub 주소도 나와있어 많은 참고가 되었다!!

 

adapter, helper, interface, model 등 사용하는 파일이 많아 헷갈릴 수 있으나

해당 코드의 작동 원리를 분석하게된 덕분에 각 구성요소의 역할들을 학습할 수 있었다.

1. 홈 화면 레이아웃 틀 잡기

- 오늘 날짜, 일기, 할일, 일정 영역 생성

 

2. 현재 위치 날씨 정보 출력

- 오늘 앱개발 종합반 4주차 강의 들으면서 날씨 정보 받아오는 방법을 알게 돼서 바로 적용!

- expo-location, openweathermap api 사용

 

3. 홈 화면 오늘 날짜, 요일 출력하기

- 자바스크립트의 Date() 이용

- 날짜를 상태(state)로 관리

 

4. 캘린더 클릭 이벤트

- 캘린더에서 날짜 선택 시 실행되는 onDayPress 내용 수정

- 선택된 날짜를 상태(state)로 관리

 

레이아웃 구성은 앞으로 계속 바뀔 수 있음..

1. VS Code에서 React Native 프로젝트 생성, Expo와 연결

 

2. GitHub에 프로젝트 연결하기 (clone, push)

 

3. StackNavigator 구현하기 (@react-navigation/stack import)

import { createStackNavigator } from '@react-navigation/stack';

const Stack = createStackNavigator();
const StackNavigator = () => {
  return(
    <Stack.Navigator>
      //페이지로 만들 컴포넌트 (추후 추가 예정)
      <Stack.Screen name="TodayPage" component={TodayPage}/>
    </Stack.Navigator>
  )
}

export default StackNavigator;

 

4. 하단탭(bottom-tab) 구현하기 (@react-navigation/bottom-tabs import)

import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';

const Tab = createBottomTabNavigator();
const BottomTabNavigator = () => {
  return(
    <Tab.Navigator>
      <Tab.Screen name="Today" component={TodayPage}/>
      <Tab.Screen name="Calendar" component={CalendarPage}/>
      <Tab.Screen name="Diary" component={DiaryPage}/>
      <Tab.Screen name="Setting" component={SettingPage}/>
    </Tab.Navigator>
  )
}

export default BottomTabNavigator;

 

5. 일기 목록 페이지 (DiaryPage.js) ScrollView 및 카드 구현

- 일기 카드를 DiaryCard.js를 별도로 만들어서 DiaryPage.js에서 불러오도록 함

 

6. Calendar 추가 (react-native-calendars import)

import { Calendar,CalendarList,Agenda } from 'react-native-calendars';

export default function CalendarPage() {
  return(
    <View style={styles.container}>
      <View style={styles.calendarContainer}>
        <Calendar style={styles.calendar}
          onDayPress={(day) => console.log(dateString)}
        ></Calendar>
      </View>
    </View>
  )
}

 

7. 환경 설정 페이지 (Setting.js) ScrollView 구현

 

 

결과 화면

홈 화면에는 오늘 날짜의 일정, To Do 리스트, 일기가 들어갈 것으로 구상하고 있다.

캘린더 화면에는 캘린더와 선택한 날짜의 일정, 할일이 나올 것으로 일단 영역을 잡아놨다. 

 

일기 목록은 인스타그램의 피드같은 느낌으로 구현할까 생각 중이다.

환경 설정은 폰트나 테마 변경 등 옵션을 좀 더 생각해봐야한다.

 

개발 첫 날인데 생각보다 많은 것을 구현했다.

가장 마음에 드는 부분은 하단탭 기능을 완성했다는 것!

각 기능별 화면 레이아웃은 앞으로 계속 구상해나갈 것이다.

또한 StackNavigation과 Bottom Navigation을 어떻게 한 화면에 담을 수 있을지 고민해봐야한다.

 

프론트엔드 개발자가 되기 위해서는 자바스크립트와 친해져야하는데

프로젝트 개발을 진행하면서 낯설게만 느껴졌던 자바스크립트와 조금씩 친해지는 느낌이 든다ㅎㅎ

현재 스파르타코딩클럽 앱개발 종합반을 수강하면서 React Native를 공부하고 있다.

수업에서 다루는 예제를 통해 학습한 내용을 복습할 겸, 추가적인 공부를 해서

나만의 앱을 만들어보고자 하는 의지가 생겼다.

 

어떤 앱을 만들까 고민하다가 어쩌면 가장 무난하고 실용성있는 앱인 일정 관리 앱을 만들기로 했다!

앱의 기능은 [일정 등록, To Do 리스트 관리, 일기 작성] 등의 기능을 포함할 예정이다.

 

간단해보이면서도 은근 다뤄야할 기술들이 많을 것 같아서

열심히 구글링하면서 필요한 라이브러리를 찾고 적용해보도록 하겠다.

 

GitHub에 해당 프로젝트 파일을 올린 상태이다.

https://github.com/askges20/ScheduleApp

 

askges20/ScheduleApp

React Native Project. Contribute to askges20/ScheduleApp development by creating an account on GitHub.

github.com

VS Code에서 GitHub 연동하기까지 은근 시간이 걸렸다.. push 오류 원인을 찾기가 어려웠다.

 

앞으로 꾸준히 개발하고 커밋해서 내 GitHub 잔디를 파릇파릇하게 물들이도록 해야지ㅎㅎ

  • Adapter 패턴 사용

 

1. RecyclerView 태그 추가

  • 원하는 화면 xml 파일에 다음과 같이 추가
<androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical" />

 

2. 데이터.java 생성

  • model 폴더
  • 변수, 생성자, Get/Set 함수 만들기 (우클릭 → Generate)

 

3. 데이터_item.xml 생성

  • 각 데이터별 카드 디자인
  • CardView를 활용함
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <androidx.cardview.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:cardBackgroundColor="#FFFFFF"
        app:cardCornerRadius="10dp"
        app:cardElevation="5dp"
        app:cardUseCompatPadding="true">

    </androidx.cardview.widget.CardView>

</LinearLayout>

 

4. 데이터Adapter.java 생성

  • extends RecyclerView.Adapter<데이터Adapter.ViewHolder>
  • public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType){}
  • public void onBindViewHolder(ViewHolder viewHolder, int position){}
  • public int getItemCount(){}
  • static class ViewHolder extends RecyclerView.ViewHolder{} 등등 추가
package org.techtown.recyclerview;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import java.util.ArrayList;

public class 데이터Adapter extends RecyclerView.Adapter<데이터Adapter.ViewHolder> {
    ArrayList<데이터> items = new ArrayList<데이터>();

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(viewGroup.getContext());
        View itemView = inflater.inflate(R.layout.person_item, viewGroup, false);

        return new ViewHolder(itemView);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder viewHolder, int position) {
        데이터 item = items.get(position);
        viewHolder.setItem(item);
    }

    @Override
    public int getItemCount() {
        return items.size();
    }

    static class ViewHolder extends RecyclerView.ViewHolder{
        TextView textView; //데이터_item.xml 내부의 요소

        public ViewHolder(View itemView){
            super(itemView);

            textView = itemView.findViewById(R.id.textView);
        }

        public void setItem(데이터 item){
            textView.setText(item.getName());
        }
    }

    public void addItem(데이터 item){
        items.add(item);
    }

    public void setItems(ArrayList<데이터> items){
        this.items = items;
    }

    public 데이터 getItem(int position){
        return items.get(position);
    }

    public void setItem(int position, 데이터 item){
        items.set(position, item);
    }
}

 

5. 원하는 화면(RecyclerView를 포함한 화면)과 관련된.java에 코드 추가

  • LayoutManager 이용해서 recyclerView 레이아웃 설정
  • Adapter 생성 후 addItem
  • RecyclerView에 Adapter을 이용해서 데이터 넘기기
RecyclerView recyclerView = findViewById(R.id.recyclerView);

        LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
        recyclerView.setLayoutManager(layoutManager);
        PersonAdapter adapter = new PersonAdapter();

        adapter.addItem(new 데이터("데이터1","데이터1속성"));
				adapter.addItem(new 데이터("데이터2","데이터2속성"));
				adapter.addItem(new 데이터("데이터3","데이터3속성"));
        recyclerView.setAdapter(adapter);

 

3학년 2학기 컴퓨터그래픽스 과목에서는 지금까지 배운 그래픽스 이론들을 활용하는

자유 주제의 텀프로젝트가 주어졌다.

개발 형태, 주제, 플랫폼 등 아무런 제한 없이 정말 자유롭게 진행하면 되는 프로젝트였고

나는 유니티3D를 이용한 게임을 개발해보았다.

프로젝트를 시작할 때 나는 유니티를 다뤄본 적이 없었고, 심지어 C#을 공부한 적도 없었다.

지금까지 C언어, 자바, 파이썬 등의 프로그래밍 언어를 배웠기 때문에 프로그래밍에 대한 지식은 있으니까

인터넷에 나온 강좌들을 찾아보면서 공부하는 동시에 개발을 해보기로 했다.

프로젝트 이름은 "치킨런", 닭이 장애물을 피해 병아리를 모아 목표지점에 도달하는 게임이다.

프로젝트 개요 및 목표

개요

장애물을 피해서 목표 지점까지 도달하는 3D 러닝 게임을 제작함

목표

- Unity3D 환경에서 게임을 제작함

- 다양한 컴퓨터그래픽스 이론을 적용하여 프로젝트를 진행함

- 닭이 모든 병아리를 모아서 목표 지점에 도착하는 러닝 게임을 제작함

프로젝트 전체 시스템 구성도

유니티에서 오브젝트에 C# 스크립트를 연결시켜 게임을 개발했다.

먼저 오브젝트는 주요 오브젝트, 장애물 및 지형, 사용자 인터페이스, 그 외의 오브젝트로 나눌 수 있다.

주요 오브젝트는 플레이어의 캐릭터인 닭, 플레이어가 모아야하는 아이템 병아리,

플레이어 시점의 카메라, 게임의 전반적인 관리를 담당하는 게임 매니저가 있다.

맵에 있는 장애물을 간략하게 표현해보았다.

스크립트는 주요 오브젝트, 장애물 및 지형, 그 외에 관련된 스크립트가 있다.

프로젝트 내 주요 기능 설명

카메라가 캐릭터를 잡아주되, 캐릭터와 일정 거리를 유지하면서 이동할 수 있도록 오프셋을 설정한다.

플레이어는 3인칭 시점에서 게임을 플레이한다.

닭의 진로를 방해하는 장애물들은 반복적으로 좌우 또는 상하로 이동한다.

장애물과 닿았을 때, 맵 밖으로 떨어졌을 때 다시 시작 지점에 리스폰된다.

동적인 효과를 주기 위해 애니메이터,애니메이터 컨트롤러를 사용했다.

플레이어의 캐릭터인 닭은 플레이어의 조작에 따라 다른 액션을 취하고

병아리들은 제자리에서 3개의 행동을 반복한다.

프로젝트 설계 방법

이 게임은 PC에서 실행하는 게임이기 때문에 키보드의 방향키를 이용하여 이동하고, 스페이스바로 점프를 한다.

게임 시작과 동시에 타이머가 시작하고 필드에 있는 병아리를 모두 모은 후 목표 지점에 도달하면 게임이 끝난다.

프로젝트 수행 결과

 

 


프로젝트 후기

유니티를 처음 사용한 것 치고는 그래도 나름 게임 같은 결과물이 나왔다.

사실 맵을 완주하는 것은 한 번도 떨어지지 않고 직진하면 1분이 걸려서 굉장히 짧고 간단한 게임이라고 할 수 있다.

그런데 게임 제작을 완료하고 주변 사람들에게 한 번 플레이해보라고 게임 파일을 보내줬는데

10, 14트만에 성공한 친구들도 있었고 대체로 게임이 어렵다는 평을 들었다. 10분 이상 플레이했다고ㅎㅎ

현재 상태에서 개선할만한 점을 몇 가지 생각해보았다.

- 단순히 제자리에서 움직이는 장애물뿐만 아니라 플레이어 캐릭터를 따라오는 인공지능적인? 방해자를 만든다.

- 커브 구간을 추가하여 맵을 다채롭게 만든다. (카메라도 같이 회전)

- 이동 방향에 따라 닭이 바라보는 방향도 자연스럽게 만든다.

- PC뿐만 아니라 모바일 플랫폼에서도 플레이 가능하도록 앱 형식으로 만든다.

 

 

https://github.com/askges20/ChickenRun

 

askges20/ChickenRun

2020-2 컴퓨터그래픽스 프로젝트. Contribute to askges20/ChickenRun development by creating an account on GitHub.

github.com

⬆ 해당 프로젝트는 현재 가상 조이스틱을 이용하여 플레이할 수 있는 앱으로 수정하였으며 GitHub에 업로드되어있다.

'App > Unity3D' 카테고리의 다른 글

컴퓨터그래픽스 프로젝트 3D 러닝 게임 "치킨런"  (0) 2021.06.15

+ Recent posts