반응형

안녕하세요.

Player Settings에서 Target API Level을 API level 29로 설정을 한 후 GoolglePlayServices에서 오류가 발생되었습니다.

 

다음 에러입니다.

Stopped polling job due to exception: System.NullReferenceException: Object reference not set to an instance of an object
  at GooglePlayServices.UnityCompat.VersionFromAndroidSDKVersionsEnum (System.String enumName, System.String fallbackPrefKey, System.Int32 fallbackValue) [0x00000] in /Users/smiles/dev/src/unity-jar-resolver/source/PlayServicesResolver/src/UnityCompat.cs:58 
  at GooglePlayServices.UnityCompat.GetAndroidTargetSDKVersion () [0x00015] in /Users/smiles/dev/src/unity-jar-resolver/source/PlayServicesResolver/src/UnityCompat.cs:116 
  at GooglePlayServices.PlayServicesResolver.CanEnableJetifierOrPromptUser (System.String titlePrefix) [0x000ae] in /Users/smiles/dev/src/unity-jar-resolver/source/PlayServicesResolver/src/PlayServicesResolver.cs:2314 
  at GooglePlayServices.PlayServicesResolver.ResolveUnsafe (System.Action`1[T] resolutionComplete, System.Boolean forceResolution, System.Boolean isAutoResolveJob, System.Boolean closeWindowOnCompletion) [0x00014] in /Users/smiles/dev/src/unity-jar-resolver/source/PlayServicesResolver/src/PlayServicesResolver.cs:1635 
  at GooglePlayServices.PlayServicesResolver+<ScheduleResolve>c__AnonStorey1E.<>m__3F () [0x00000] in /Users/smiles/dev/src/unity-jar-resolver/source/PlayServicesResolver/src/PlayServicesResolver.cs:1604 
  at GooglePlayServices.PlayServicesResolver.ExecuteNextResolveJob () [0x00069] in /Users/smiles/dev/src/unity-jar-resolver/source/PlayServicesResolver/src/PlayServicesResolver.cs:1461 
  at GooglePlayServices.PlayServicesResolver.ScheduleResolve (System.Boolean forceResolution, System.Boolean closeWindowOnCompletion, System.Action`1[T] resolutionCompleteWithResult, System.Boolean isAutoResolveJob) [0x000a7] in /Users/smiles/dev/src/unity-jar-resolver/source/PlayServicesResolver/src/PlayServicesResolver.cs:1617 
  at GooglePlayServices.PlayServicesResolver.AutoResolve (System.Action resolutionComplete) [0x00017] in /Users/smiles/dev/src/unity-jar-resolver/source/PlayServicesResolver/src/PlayServicesResolver.cs:1134 
  at GooglePlayServices.PlayServicesResolver.<ScheduleAutoResolve>m__47 () [0x00016] in /Users/smiles/dev/src/unity-jar-resolver/source/PlayServicesResolver/src/PlayServicesResolver.cs:1113 
  at Google.RunOnMainThread.ExecutePollingJobs () [0x0004a] in /Users/smiles/dev/src/unity-jar-resolver/source/VersionHandlerImpl/src/RunOnMainThread.cs:300 
UnityEngine.Debug:LogError(Object)
Google.RunOnMainThread:ExecutePollingJobs() (at /Users/smiles/dev/src/unity-jar-resolver/source/VersionHandlerImpl/src/RunOnMainThread.cs:303)
Google.RunOnMainThread:<ExecuteAll>m__A() (at /Users/smiles/dev/src/unity-jar-resolver/source/VersionHandlerImpl/src/RunOnMainThread.cs:414)
Google.RunOnMainThread:RunAction(Action) (at /Users/smiles/dev/src/unity-jar-resolver/source/VersionHandlerImpl/src/RunOnMainThread.cs:234)
Google.RunOnMainThread:ExecuteAll() (at /Users/smiles/dev/src/unity-jar-resolver/source/VersionHandlerImpl/src/RunOnMainThread.cs:406)
UnityEditor.EditorApplication:Internal_CallUpdateFunctions() (at C:/buildslave/unity/build/Editor/Mono/EditorApplication.cs:303)

검색을 해보니, Play Services Resolver를 업데이트 해야 해결된다고 합니다.

 

이리저리해보다가 안되서... API level 28로 낮춘 후 재시작하고, 다시 타겟 API 레벨을 상위로 올렸습니다.

 

근본적인 문제해결하면 다시 올리겠습니다.

반응형
반응형

안녕하세요

포맷 후 앱을 빌드하다가 Target Android SDK installed 에러가 발생했습니다.

내용은 Android SDK does not include your Target SDK of 29. Please use the Android SDK Manager to install your target SDK version. Restart Unity after SDK installation for the changes to take effect. 였습니다.

번역하면 "Android SDK에는 Target SDK 29가 포함되어 있지 않습니다. Android SDK Manager를 사용하여 대상 SDK 버전을 설치하십시오. 변경 사항을 적용하려면 SDK 설치 후 Unity를 다시 시작하세요" 입니다.

target 버전의 SDK가 설치가 안되었다는 뜻인데, 설치하려면 안드로이드 스튜디오로 들어가서 File - Settings로 들어갑니다.

좌측에 SDK를 선택해주고 해당 안드로이드 SDK를 설치합니다.

 

설치가 완료되면 unity를 재시작한 후 빌드를 합니다.

 

반응형
반응형

안녕하세요.

개발을 하다 보면 터치 혹은 마우스 클릭으로 오브젝트를 선택해야 하는 경우가 있습니다.

1. 3D 오브젝트 선택하기 

새로 프로젝트를 만들고 3D 오브젝트를 생성해줍니다.

저는 Cube, Capsule, Sphere, Cyinder를 생성한 후 적당한 위치로 이동시켜봅니다.

그리고 빈 오브젝트를 하나 생성해서 스크립트를 다음과 같이 작성합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class SelectObjectManager : MonoBehaviour
{
    Vector3 m_vecMouseDownPos;
 
    void Update()
    {
#if UNITY_EDITOR
        // 마우스 클릭 시
        if (Input.GetMouseButtonDown(0))
#else
        // 터치 시
        if (Input.touchCount > 0)
#endif
        {
 
#if UNITY_EDITOR
            m_vecMouseDownPos = Input.mousePosition;
#else
            m_vecMouseDownPos = Input.GetTouch(0).position;
            if(Input.GetTouch(0).phase != TouchPhase.Began)
                return;
#endif
            // 카메라에서 스크린에 마우스 클릭 위치를 통과하는 광선을 반환합니다.
            Ray ray = Camera.main.ScreenPointToRay(m_vecMouseDownPos);
            RaycastHit hit;
 
            // 광선으로 충돌된 collider를 hit에 넣습니다.
            if(Physics.Raycast(ray, out hit))
            {
                // 어떤 오브젝트인지 로그를 찍습니다.
                Debug.Log(hit.collider.name);
 
                // 오브젝트 별로 코드를 작성할 수 있습니다.
                if (hit.collider.name == "Cube")
                    Debug.Log("Cube Hit");
                else if (hit.collider.name == "Capsule")
                    Debug.Log("Capsule Hit");
                else if (hit.collider.name == "Sphere")
                    Debug.Log("Sphere Hit");
                else if (hit.collider.name == "Cylinder")
                    Debug.Log("Cylinder Hit");
            }
 
        }
    }
}
 
cs

실행하면 오브젝트를 클릭할 때마다 로그가 찍히는 것을 확인할 수 있습니다.

 

2. 2D 오브젝트 선택하기

같은 방식으로 2D 오브젝트를 Square, Circle, Capsule을 생성해줍니다.

2D 오브젝트는 생성할 때 기본적으로 Collider가 없기 때문에 오브젝트에 맞게 Collider를 넣어줘야 합니다.

Square에는 Box Collider 2D를 넣습니다.
Circle에는 Circle Collider 2D를 넣어줍니다.
Capsule에는 Capsule Collider 2D를 넣어줍니다.

그리고 스크립트를 넣을 빈 오브젝트도 생성한 후 다음 스크립트를 넣어줍니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class SelectObjectManager : MonoBehaviour
{
    Vector3 m_vecMouseDownPos;
 
    void Update()
    {
#if UNITY_EDITOR
        // 마우스 클릭 시
        if (Input.GetMouseButtonDown(0))
#else
        // 터치 시
        if (Input.touchCount > 0)
#endif
        {
 
#if UNITY_EDITOR
            m_vecMouseDownPos = Input.mousePosition;
#else
            m_vecMouseDownPos = Input.GetTouch(0).position;
            if(Input.GetTouch(0).phase != TouchPhase.Began)
                return;
#endif
            
 
            // 마우스 클릭 위치를 카메라 스크린 월드포인트로 변경합니다.
            Vector2 pos = Camera.main.ScreenToWorldPoint(m_vecMouseDownPos);
 
            // Raycast함수를 통해 부딪치는 collider를 hit에 리턴받습니다.
            RaycastHit2D hit = Physics2D.Raycast(pos, Vector2.zero);
 
            if (hit.collider != null)
            {
                // 어떤 오브젝트인지 로그를 찍습니다.
                Debug.Log(hit.collider.name);
 
                // 오브젝트 별로 코드를 작성할 수 있습니다.
                if (hit.collider.name == "Square")
                    Debug.Log("Square Hit");
                else if (hit.collider.name == "Circle")
                    Debug.Log("Circle Hit");
                else if (hit.collider.name == "Capsule")
                    Debug.Log("Capsule Hit");
            }
 
        }
    }
}
 
cs

클릭할 때마다 로그가 찍히는 것을 확인할 수 있습니다.

 

3. 2D오브젝트가 선택이 제대로 안될 때

카메라 설정에 Projection이 "Perspective"로 되어있으면 제대로 선택이 안됩니다.

꼭 2D 일 때는 "Orthographic"로 설정해야 합니다.

Perspective : 원근감이 적용됩니다. (주로 3D)
Orthographic : 직각 투영으로 원근감이 없습니다.(주로 2D)

반응형
반응형

안녕하세요.

지난 시간에 이어서 디스코드 봇 만들기를 위한 코드작성에 대해 알아보도록 하겠습니다.

 

디스코드 봇 만들기(1) - 봇생성

안녕하세요. 디스코드 봇을 만드는 법에 대해 알아보도록 하겠습니다. 일단 디스코드 사이트로 들어갑니다. https://discord.com/ Discord | Your Place to Talk and Hang Out Discord is the easiest way to talk..

scvtwo.tistory.com

언어는 여러가지가 있지만, 파이썬을 이용해 보겠습니다.

discord.py 라이브러리 패키지를 설치가 필요합니다.

콘솔창에서 다음 명령어를 입력합니다.

pip install discord.py

그리고 다음과 같이 코드를 작성합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import discord
from discord.ext import commands
 
bot = commands.Bot(command_prefix='#')
 
@bot.event
async def on_ready():
    print(f'Login bot: {bot.user}')
 
@bot.command()
async def hello(message):
    await message.channel.send('Hi!')
 
bot.run('#OTY5MTUyNz')
cs

코드를 설명하면, 4번째 줄은 어떤 봇을 만들것인지 설정합니다. "command_prefix='#'"은 '#'을 앞에 넣어서 명령어를 실행하겠다는 의미입니다.(ex. #info, #hi 등등)

@bot.event는 이벤트 함수를 만들겠다는 것이고 함수 앞에 async가 붙는건 비동기로 실행되는 함수라는 의미입니다.

on_ready함수는 봇이 시작될 때 실행되는 이벤트함수입니다.

@bot.command은 command함수를 만들겠다는 의미입니다. def hello함수는 "#hello"를 대화창에 입력했을 경우 실행되는 함수 입니다. 

"message.channel.send('Hi!')"는 메세지가 온 채널에 'Hi!'라는 내용을 보내줍니다. 채팅창에 #hello를 사용자가 보내면 "Hi!" 메세지를 봇이 보내게됩니다.

bot.run('#OTY5MTUyNz')은 token값인 #OTY5MTUyNz 대신 봇을 생성할 때 만들어진 토큰값을 넣어주면 됩니다.

토큰값을 잃어버렷다면, 디스코드의 개발자 페이지에 들어가서 Reset Token을 버튼을 누르면 다시 생성 할 수 있습니다.

실행하면 bot이 온라인 상태로 변경되는 것을 확인할 수 있습니다.

반응형
반응형

안녕하세요.

디스코드 봇을 만드는 법에 대해 알아보도록 하겠습니다.

일단 디스코드 개발자 포털로 들어갑니다.

https://discord.com/developers/applications

 

Discord Developer Portal — API Docs for Bots and Developers

Integrate your service with Discord — whether it's a bot or a game or whatever your wildest imagination can come up with.

discord.com

 

그 후 좌측에 있는 Applications을 클릭합니다.

 

이제 로그인하려고 뜹니다.

아직 회원가입을 안했다면, 회원가입을 하고 로그인을 합니다.

 

그럼 이제 Applications페이지로 들어가게 됩니다.

새로운 Application을 생성해야하기 때문에 "New Application" 버튼을 클릭합니다.

 

이름을 넣어주고 "Create"버튼을 클릭합니다.

아직 봇이 생성된 것은 아닙니다.

좌측에 "Bot"을 클릭합니다.

클릭 후 나온 페이지에서 "Add Bot"버튼을 클릭합니다.

봇을 추가한다는 메세지가 나오는데, "Yes, do it!" 버튼을 클릭합니다.

 

이제 봇이 추가되었습니다.

추후에 코드에서 생성된 봇을 연결하기위해서는 토큰이 필요합니다.

"Reset Token"버튼 클릭하여 토큰 값을 얻습니다.

 

다음 메세지창에서 "Yes, do it!"버튼을 클릭합니다.

토큰은 나중에 코드상에서 봇에 접속하기 위한 고유키이므로, OTY5...로 시작하는 토큰을 복사하고 따로 잘보관해둡니다.(토큰이 유출되면 다른 PC에서도 접근할 수 있습니다)

 

봇을 디스코드 서버에 초대해야 합니다.

다음과 같이해서 URL을 생성합니다.

좌측에 "OAuth2"를 클릭 후 "bot"을 체크하고 아래 BOT 권한을 체크하면 URL이 생성됩니다.

URL을 복사하여 주소창에 넣으면 디스코드 어느 서버 초대할지 선택하라고 합니다.

초대하기 전에 미리 서버를 추가해야합니다.

 

오프라인으로 봇이 서버에 입장했습니다.

오프라인인 봇을 온라인으로 만드는 방법은 다음 페이지를 참조해주세요.

 

디스코드 봇 만들기(2) - 코드(python)

안녕하세요. 지난 시간에 이어서 디스코드 봇 만들기를 위한 코드작성에 대해 알아보도록 하겠습니다. 언어는 여러가지가 있지만, 파이썬을 이용해 보겠습니다. discord.py 라이브러리 패키지를

scvtwo.tistory.com

 

반응형
반응형

안드로이드에서 Webview로 웹사이트를 호출하면 err_cleartext_not_permitted 에러가 발생했습니다.

http://로 시작하는 웹페이지에 접근할 때 보안문제로 인해 발생하게 됩니다.

Android 9.0이상(API 수준 28)에서 발생합니다.

 

가장 빠른 해결 방법은 app\manifests\AndroidManifest.xml에 항목을 다음 항목을 추가하면 됩니다.

android:usesCleartextTraffic="true"

 

반응형
반응형

안녕하세요.

Unity와 Firebase를 연동한 후 Firebase기능 중 하나인 데이터베이스를 연동해보도록 하겠습니다.

 

1. Firebase셋팅

Firebase Colsole에 접속합니다.

https://console.firebase.google.com/?hl=ko 

 

로그인 - Google 계정

하나의 계정으로 모든 Google 서비스를 Google 계정으로 로그인

accounts.google.com

 

프로젝트를 추가합니다.

프로젝트 이름을 입력한 후 계속 버튼을 클릭해줍니다.

 

애널리틱스 사용여부를 확인한 후 계속 버튼을 클릭합니다.

애널리틱스 계정 선택을 하라고 나옵니다.

따로 계정이 있다면 설정해주고, 없다면 Default Account for Firebase를 선택한 후 프로젝트 만들기를 클릭합니다.

잠시 기다린 후 준비되었다는 화면이 나오면 계속 버튼을 클릭합니다.

 

프로젝트 창으로 이동되는데, 여기서 Unity마크를 클릭합니다.

 

앱을 등록합니다.

IOS, Android를 각각 등록합니다.

 

유니티 ProjectSettings에 패키지네임을 적으면 됩니다.

 

IOS번들ID와 Android 패키지이름을 넣으면 앱등록 버튼이 활성화됩니다. 

클릭해줍니다.

google-services.json, GoogleService-Info.plist 구성 파일 다운로드를 해서 Unity프로젝트의 Assets폴더에 넣어줘야합니다.

넣어준 후 다음 버튼을 클릭합니다.

Firebase SDK 추가를 합니다.

Firebase Unity SDK(Zip)파을 다운로드 받은 후 압축을 풀어줍니다.

압축을 풀어주면 추후에 변경될지도 모르겠지만, 지금 제가 받은 파일에 dotnet3, dotnet4폴더로 구성되어있는데

버전에 따라 다른 패키지를 임포트합니다.(Unity 5.x버전은 dotnet3 패키지, Unity 2017.x이상에서는 dotnet4 패키지)

애널리틱스를 위에서 사용하기로 했으므로 FirebaseAnalytics.unitypackage를 임포트한 후 Database를 사용하기 위해 FirebaseDatabase.unitypackage도 임포트합니다. 기타 필요한 패키지를 임포트합니다.

 Assets(애셋) - Import Package(패키지 가져오기) - Custom Package(커스텀 패키지)로 추가 가능합니다.

그 후 Firebase Colsole로 돌아와서 다음을 클릭합니다.

이제 설정이 끝났습니다.

콘솔로 이동을 클릭합니다.

 

2. Firebase에서 Database추가

이제 Database를 추가해줍니다.

Realtime Database를 클릭합니다.

 

데이터 베이스 만들기를 클릭합니다.

다음으로 위치설정이있는데, 대한민국은 없기 때문에 미국으로 설정합니다.

테스트를 위해 테스트 모드에서 시작으로 설정합니다.

이제 데이터 베이스를 추가할 수 있도록 셋팅되었습니다.

 

3. 유니티(Unity)에 적용

간단하게 유니티에서 데이터 저장과 로드를 적용해보도록 하겠습니다.

빈오브젝트를 생성하고 스크립트를 넣습니다.

CFirebase.cs 코드는 다음과 같습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Firebase;
using Firebase.Database;
using Firebase.Extensions;
 
public class CFirebase : MonoBehaviour
{
    DatabaseReference m_Reference;
    
    void Start()
    {
        m_Reference = FirebaseDatabase.DefaultInstance.RootReference;
 
        //WriteUserData("0", "aaaa");
        //WriteUserData("1", "bbbb");
        //WriteUserData("2", "cccc");
        //WriteUserData("3", "dddd");
 
        ReadUserData();
 
 
    }
 
    void ReadUserData()
    {
        FirebaseDatabase.DefaultInstance.GetReference("users")
            .GetValueAsync().ContinueWithOnMainThread(task =>
        {
            if (task.IsFaulted)
            {
                // Handle the error...
            }
            else if (task.IsCompleted)
            {
                DataSnapshot snapshot = task.Result;
                // Do something with snapshot...
            for ( int i = 0; i < snapshot.ChildrenCount; i++)
                Debug.Log(snapshot.Child(i.ToString()).Child("username").Value);
              
            }
        });
    }
 
    void WriteUserData(string userId, string username)
    {
        m_Reference.Child("users").Child(userId).Child("username").SetValueAsync(username);
    }
 
}
 
cs

 

WriteUserData함수를 통한 데이터를 저장하면, Database는 다음과 같이 저장됩니다. 

ReadUserData함수를 통해 데이터를 로드하면 디버그로그로 다음과 같이 출력되는 것을 확인할 수 있습니다.

반응형
반응형

안녕하세요.

유니티에서 빌드해서 apk를 생성 후 블루스택에서 확인하려고 했지만 실행되지 않는 문제가 발생하여, 공유하려고 합니다.

Unity 2020.3.19f1에서 발생한 문제였습니다.

휴대폰에 넣었을 때는 문제가 발생하지 않는데 블루스택에 설치하면 까만 화면만 나오고 더 이상 진행되지 않았습니다.

휴대폰에 넣어서 확인하면 되긴 하지만, 아무래도 블루스택에서 다양한 해상도나 환경을 만들 수 있기 때문에 해결하고 싶었습니다.

제가 가지고 있는 이전버전(2019.2.9f1)으로 할 때는 발생하지 않은 문제였습니다.

그래서 시험삼아 2019.2.9f1로 프로젝트를 새로 생성한 후 2020.3.19f1로 업그레이드해서 빌드를 해보았습니다.

그랬더니 정상적으로 블루스택에서 실행이 되었습니다.

먼가 설정문제인 것 같아서 두 프로젝트의 Project Setting을 확인했습니다.

처음부터 2020.3.19f1로 생성한 프로젝트에만 Optimized Frame Pacing이 체크되어있었습니다.

그래서 체크 해제를 한 후 다시 빌드하니 정상적으로 블루스택에서 실행되었습니다.

2019.2 버전부터 지원하는 기능이었는데, 보통 꺼져있는 것이 디폴트 값이었는데, 2020.3.19f1에서는 켜지는 것이 디폴드 값으로 변한 것 같습니다.

https://unity.com/kr/releases/2019-2/platforms-and-editor#optimized-frame-pacing-android

 

플랫폼과 에디터 | Unity

플랫폼과 일반 에디터 워크플로에 대한 Unity 2019.2의 새로운 기능에 대해 알아보세요. AR 파운데이션에 대한 업데이트와 모바일 개발자들을 위한 추가 툴이 새롭게 제공됩니다.

unity.com

 

반응형
반응형

안녕하세요.

오늘은 앱바(App Bar)에 제목을 변경하거나 버튼을 추가하는 법에 대해 알아보도록 하겠습니다.

앱바는 앱의 아래 부분을 말합니다.
앱바는 액션바(Action Bar)라고도 합니다.


최근 버전이 올라가면서 다양한 기능을 맞춤 설정을 할 수 있기 때문에 툴바를 사용합니다. 

일단 다음과 같은 순서로 앱바 기능에 대해 알아보도록 하겠습니다.

1. 앱바 제목 변경

2. 앱바 색상 변경하기

3. 버튼 추가

4. 버튼에 따른 이벤트 추가

5. 앱바에 아이콘추가

6. 앱바 보이기/안보이기

기본설정은 "Empty Activity"상태로 하겠습니다.

 

 

1. 앱바 제목 변경

manifests/AndroidManifest.xml파일에서 label값을 변경해주면 됩니다.

그냥 값을 변경해줘도 적용이 되지만 위에 이미지와 같이 "@string/app_name" 경로로 표기하여 경로에 있는 스트링을 변경해줘도 됩니다.

res/values/strings.xml값을 변경하면 제목을 변경할 수 있습니다.

하지만 이렇게 하면 앱이 실행 전에만 바꿀 수 있습니다.

앱이 실행중일 때 제목을 바꾸려면 코드상에서 수정해야 합니다.

다음 코드를 사용합니다.

getSupportActionBar().setTitle("Change Title");

MainActivity.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.testcompany.appbar;
 
import androidx.appcompat.app.AppCompatActivity;
 
import android.os.Bundle;
 
public class MainActivity extends AppCompatActivity {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        // 실행 중 제목을 변경할 수 있습니다.
        getSupportActionBar().setTitle("Change Title");
    }
}
cs

 

2. 앱바 색상 변경하기

/res/values/themes.xml을 열어보면 다음과 같이 되어있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<resources xmlns:tools="http://schemas.android.com/tools">
    <!-- Base application theme. -->
    <style name="Theme.Appbar" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
        <!-- Primary brand color. -->
        <item name="colorPrimary">@color/purple_500</item>
        <item name="colorPrimaryVariant">@color/purple_700</item>
        <item name="colorOnPrimary">@color/white</item>
        <!-- Secondary brand color. -->
        <item name="colorSecondary">@color/teal_200</item>
        <item name="colorSecondaryVariant">@color/teal_700</item>
        <item name="colorOnSecondary">@color/black</item>
        <!-- Status bar color. -->
        <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
        <!-- Customize your theme here. -->
    </style>
</resources>
cs

다음 부분을 수정하면 됩니다.

<item name="colorPrimary">@color/purple_500</item>

<item name="colorPrimaryVariant">@color/purple_700</item>

"@color/purple_500", "@color/purple_700" 는 "/res/value/colors.xml" 파일을 확인해보면 됩니다.

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="purple_200">#FFBB86FC</color>
    <color name="purple_500">#FF6200EE</color>
    <color name="purple_700">#FF3700B3</color>
    <color name="teal_200">#FF03DAC5</color>
    <color name="teal_700">#FF018786</color>
    <color name="black">#FF000000</color>
    <color name="white">#FFFFFFFF</color>
</resources>
cs

 

colorPrimary, colorPrimaryVariant는 각각 다음 위치를 가리킵니다.

3. 버튼 추가

3-1. 메뉴 XML 파일 추가

버튼을 추가하기 위해서는 일단 메뉴(Meunu) XML 파일을 먼저 추가해야 합니다.

다음과 같이 합니다.

res폴더를 선택합니다.

File - New - Android Resource File을 선택합니다.

그리고 다음 나오는 창에서 "Resource type"항목의 리스트 박스에서 Menu를 선택합니다.

다음 파일 이름을 입력한 후 OK 버튼을 누릅니다.

res/menu/main_menu.xml이 추가되었습니다.

3-2 메뉴 XML 파일 코드 수정

main_menu.xml에 코드를 추가하여 버튼을 추가할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
  <item
      android:id="@+id/search_action"
      android:icon="@android:drawable/ic_menu_search"
      android:title="Search"
      app:showAsAction="always"/>
 
</menu>
cs

"<item ..... />" 부분을 추가해서 버튼을 더 추가할 수 있습니다.

item구문에서 showAsAction항목에 따라 어떻게 보이게 할 건지 설정할 수 있습니다.

설정한 값에 따라 다음과 같이 표시됩니다.

always - 항상 표시해야 한다면 사용합니다. 

ifRoom - 표시할 공간이 있는 경우에만 표시되고 아니면 더보기 메뉴에 표기되게 됩니다.

withText - 아이콘과 텍스트(android:tilte로 지정한 텍스트)가 같이 표기됩니다. 파이프로 다른 설정과 포함할 수 있습니다. (설명은 이렇게 적혀있는데, 제가 해봤을 때 넣으면 never와 같이 더보기 메뉴에만 추가됩니다. )

never - 앱바에 배치하지 않고, 더보기 메뉴에 표기합니다.

3-3 앱바에 표기하기

막상 위에까지 하고 실행시켜보면 앱바에 표기가 안됩니다.

아직 코드에서 연결을 안 했기 때문입니다.

mainActivity.java파일에서 다음 코드를 추가합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.testcompany.appbar;
 
import androidx.appcompat.app.AppCompatActivity;
 
import android.os.Bundle;
import android.view.Menu;
 
public class MainActivity extends AppCompatActivity {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    @Override
    public boolean onCreateOptionsMenu(Menu menu)    {
        getMenuInflater().inflate(R.menu.main_menu, menu);
        return true;
    }
}
cs

 

R.menu.main_menu는 main_menu.xml을 의미합니다.

실행해보면 버튼이 추가되어있는 것을 확인할 수 있습니다.

4. 버튼에 따른 이벤트 추가

이제 버튼에 따른 이벤트를 추가해야합니다.
onOptionsItemSelected함수를 추가하여 구현할 수 있습니다.
다음과 같이 추가하면 됩니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.testcompany.appbar;
 
import androidx.appcompat.app.AppCompatActivity;
 
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;
 
public class MainActivity extends AppCompatActivity {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    @Override
    public boolean onCreateOptionsMenu(Menu menu)    {
        getMenuInflater().inflate(R.menu.main_menu, menu);
        return true;
    }
 
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId())
        {
            case R.id.search_action:
                Toast.makeText(getApplicationContext(), "Search Action", Toast.LENGTH_SHORT).show();
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }
}
cs

 

5. 앱바에 아이콘 추가

onCreate에 다음 구문을 추가하면 됩니다.

    getSupportActionBar().setIcon(R.drawable.ic_baseline_4k_24);
    getSupportActionBar().setDisplayUseLogoEnabled(true);
    getSupportActionBar().setDisplayShowHomeEnabled(true);

R.drawable.ic_baseline_4k_24 는 아이콘으로 설정할 리소스입니다.

6. 앱바 보이기/안보이기

코드에서 다음 구문을 사용할 수 있습니다.

getSupportActionBar().show();     // 보이도록 합니다.
getSupportActionBar().hide();      // 안 보이도록 합니다.

반응형
반응형

안녕하세요. 

이전시간에 안드로이드와 Firebase와 연결을 해보았습니다.

이번에는 안드로이드 스튜디오에서 Firebase의 Database를 연동해서 DB 데이터 저장 및 로드를 해보도록 하겠습니다.

일단 Database 추가를 위해서 Firebase colsole으로 들어갑니다.

 

 

 

로그인 - Google 계정

하나의 계정으로 모든 Google 서비스를 Google 계정으로 로그인

accounts.google.com

 

추가한 프로젝트를 클릭합니다.

왼쪽에 Firestore Database와 Realtime Database가 있는데, 저는 Realtime Database로 셋팅해보겠습니다.

"Realtime Database"를 클릭합니다.

Firestore Database와 Realtime Database의 차이가 궁금하시다면, 다음 페이지를 참조하시면 될 것 같습니다.

https://firebase.google.com/docs/database/rtdb-vs-firestore?authuser=0 

 

데이터베이스 선택: Cloud Firestore 또는 실시간 데이터베이스  |  Firebase

Firebase는 실시간 데이터 동기화를 지원하며 클라이언트에서 액세스할 수 있는 2가지 클라우드 기반 데이터베이스 솔루션을 제공합니다. Cloud Firestore는 모바일 앱 개발을 위한 Firebase의 최신 데이

firebase.google.com

 

데이터베이스 만들기를 클릭합니다.

 

대한민국은 없기때문에 Default인 미국으로 설정하고 다음을 클릭합니다.

 

테스트를 하기위해서 테스트 모드에서 시작으로 변경해주고 사용설정을 클릭해줍니다.

잠시 기다리면 데이터베이스를 추가할 수 있는 창이 생성되었습니다.

 

이제 안드로이드 스튜디오에서 관련 코드를 넣습니다.

다음 코드로 데이터베이스를 얻어옵니다.

DatabaseReference m_Database;

m_Database = FirebaseDatabase.getInstance().getReference();

 

저장은 다음과 같은 코드로 넣습니다.

                        m_Database.child("child1").child("child2").child("child3").setValue("child3 저장할데이터")
                        .addOnSuccessListener(new OnSuccessListener<Void>() {
                            @Override
                            public void onSuccess(Void aVoid) {
                                // 저장 성공 시 처리
                            }
                        })
                        .addOnFailureListener(new OnFailureListener() {
                            @Override
                            public void onFailure(@NonNull Exception e) {
                                // 저장 실패 시 처리
                            }
                        });

                       

로드는 다음과 같은 코드로 할 수 있습니다.

ValueEventListener postListener = new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                // 데이터를 불러올 때 처리
                int nCount = (int)dataSnapshot.getChildrenCount();
                String name;
                for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) {
                  // 저장된 데이터를 하나씩 얻어옵니다.
                    if(postSnapshot.child("child3").getValue(String.class) != null) {

                        name = postSnapshot.child("child3").getValue(String.class);
                    }

                }
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {
                // 데이터 불러오기 실패 시 처리
            }
        };
       // child3으로 정렬합니다.
        Query sortbyAge = m_Database.child("child1").orderByChild("child3");
        sortbyAge.addListenerForSingleValueEvent(postListener);

 

실제적으로 안드로이드 스튜디오에서 적용은 다음과 같이합니다.

MainActivity.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
package com.Testcompany.test;
 
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
 
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;
 
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.Query;
import com.google.firebase.database.ValueEventListener;
 
import java.util.ArrayList;
 
public class MainActivity extends AppCompatActivity {
    private DatabaseReference m_Database;
    EditText m_edittext1;
    Button m_button1;
    ListView m_listview;
    int m_nCount = 0;
    ArrayList<String> m_arritems;
    ArrayAdapter m_adapter;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        m_Database = FirebaseDatabase.getInstance().getReference();
        m_edittext1 = findViewById(R.id.edit1);
        m_button1 = findViewById(R.id.button1);
        m_listview =findViewById(R.id.listview1);
 
        m_arritems = new ArrayList<String>() ;
        m_adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, m_arritems);
 
 
        m_listview.setAdapter(m_adapter);
 
        readUser();
 
        m_button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String data1 = m_edittext1.getText().toString();
 
                m_Database.child("users").child("Data"+m_nCount).child("username").setValue(data1)
                        .addOnSuccessListener(new OnSuccessListener<Void>() {
                            @Override
                            public void onSuccess(Void aVoid) {
                                Toast.makeText(MainActivity.this"저장 성공.", Toast.LENGTH_SHORT).show();
                                readUser();
                                m_edittext1.setText("");
                            }
                        })
                        .addOnFailureListener(new OnFailureListener() {
                            @Override
                            public void onFailure(@NonNull Exception e) {
                                Toast.makeText(MainActivity.this"저장 실패.", Toast.LENGTH_SHORT).show();
                            }
                        });
 
            }
 
        });
 
 
    }
 
 
    private void readUser(){
        ValueEventListener postListener = new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                m_arritems.clear();
                m_nCount = (int)dataSnapshot.getChildrenCount();
                String name;
                for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) {
                    if(postSnapshot.child("username").getValue(String.class!= null) {
                        name = postSnapshot.child("username").getValue(String.class);
                        m_arritems.add(name);
                    }
                }
                m_adapter.notifyDataSetChanged();
            }
 
            @Override
            public void onCancelled(DatabaseError databaseError) {
                Log.w("FirebaseDatabase","onCancelled", databaseError.toException());
            }
        };
        Query sortbyAge = m_Database.child("users").orderByChild("users");
        sortbyAge.addListenerForSingleValueEvent(postListener);
 
    }
}
cs

activity_main.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
 
    <EditText
        android:id="@+id/edit1"
        android:layout_width="189dp"
        android:layout_height="70dp"
        android:layout_marginEnd="36dp"
        android:layout_marginRight="36dp"
        android:layout_marginTop="28dp"
        android:ems="10"
        android:inputType="textPersonName"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.236"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
 
    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="44dp"
        android:text="Button"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.861"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
 
    <ListView
        android:id="@+id/listview1"
        android:layout_width="409dp"
        android:layout_height="300dp"
        android:layout_marginTop="104dp"
        app:layout_constraintTop_toTopOf="parent"
        tools:layout_editor_absoluteX="1dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
cs

 

 

반응형
반응형

오늘은 안드로이드 스튜디오와 Firebase를 연동하는 방법에 대해 알려드리도록 하겠습니다.

과정은 다음과 같습니다.

1. Firebase Colsole에서 Firebase 프로젝트 만들기

firebase 콘솔에 접속한 후 구글아이디로 로그인합니다.

 

로그인 - Google 계정

하나의 계정으로 모든 Google 서비스를 Google 계정으로 로그인

accounts.google.com

이 페이지에서 일단 프로젝트를 추가해줘야합니다. 

프로젝트 추가를 클릭합니다.

프로젝트 이름을 지정한 후 "계속" 버튼을 클릭합니다.

Google 애널리틱스 를 사용할지를 정한 후 계속 버튼을 클릭합니다.

애널리틱스 계정을 선택합니다. 

계정이 따로 없다면, Default Account for Firebase로 설정한 후 "프로젝트 만들기" 버튼을 클릭합니다.

프로젝트 생성 중 화면이 나오면 완료될 때 까지 기다립니다.

기다리면, 새 프로젝트가 준비되었다고 나옵니다.

"계속" 버튼을 클릭합니다.

 

프로젝트 창으로 이동하게 되는데, 여기서는 어떤 플랫폼에서 사용할지 지정할 수 있습니다.

안드로이드 모양 버튼을 클릭합니다.

Android 패키지 이름, 앱 닉네임(선택), 디버그 서명 인증서 SHA-1(선택)을 입력해야합니다.

Android 패키지 이름은 처음에 안드로이드 스튜디오에서 프로젝트 만들때 지정된 패키지 이름을 넣습니다.

MainActivity.java파일에서 바로 확인가능합니다.

앱 닉네임, 디버그 서명 인증서 SHA-1은 넣습니다.

"앱등록" 버튼을 누릅니다.

"google-services.json 다운로드" 버튼을 누릅니다.

설명과 같이 다운로드 받은 google-services.json을 프로젝트의 app에 넣어줍니다.

이제 안드로이드 스튜디오와 Firebase를 연결을 해야합니다.

build.gradle파일을 수정해야합니다.

앱수준의 build.gradle, 프로젝트 수준의 build.gradle을 수정해야합니다.

밑에 이미지에서는 1번이 앱수준의 build.gradle, 2번이 프로젝트 수준의 build.gradle입니다.

1번 앱 수준의 build.gradle은 다음과 같이 추가가 되어야합니다.

2번 프로젝트 수준의 build.gradle은 다음과 같이 추가가 되어야합니다.

 

이 후 동기화를 합니다.

 

이제 Firebase 콘솔에서 설정은 끝났습니다.

 

이제 연결을 확인합니다.

안드로이드 스튜디오 메뉴에서 Tool-Firebase를 클릭합니다.

Firebase 메뉴가 나옵니다.

저는 실시간 디비를 통해 연결을 확인해보도록 하겠습니다.

3번째 Realtime Database를 클릭해봅니다.

Save and retrieve data가 생깁니다. 클릭합니다.

 

다음과 같은 화면이 나옵니다.

1, 2번이 모두 체크가 되어야 합니다.

2번은 아직 체크가 안되었기 때문에 Add to Realtime Database to your app을 클릭합니다.

클릭하면 메세지 박스가 하나 뜹니다. app/build.gradle파일에 database 관련 라이브러리 관련 구문을 추가하겠냐고 물어봅니다.

Accept Changes를 클릭합니다.

잠시 기다리면 "Dependencies set up correctly"로 변경된 것을 볼 수 있습니다.

안드로이드 스튜디오에서 Firebase와 연결되었습니다.

반응형
반응형

안녕하세요.

코딩을 하다 보면 시간에 관련된 함수를 많이 사용하게 됩니다.

항상 구글링으로 찾아보았었는데, 이번 기회에 한번 정리해봅니다.

공통으로 헤더 파일은 time.h를 포함시켜줘야 합니다.

 

time

현재시간을 time_t로 리턴합니다.

함수 원형

time_t time(time_t*  _Time);

반환형도 있고, 리턴 값도 있습니다. 리턴 값으로 받아도 되고, 파라미터에 time_t변수의 포인트를 넘겨서 받을 수도 있습니다.

time_t는 32비트 플랫폼에서는 long으로 64비트 플랫폼에서는 __int64로 typedef 되어 있습니다.

예제

1
2
3
4
5
6
7
8
9
10
11
12
13
#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS
#endif
 
#include <stdio.h>
#include <time.h>
 
void main()
{
    time_t t = 0;
    t = time(NULL);        // time(&t);로 해도 같습니다.
    printf("%lld\n", t);
}
cs

 

localtime

time_t타입을 이용해서 tm구조체로 만듭니다.

tm구조체는 멤버 변수로 년, 월, 날, 시간, 분, 초가 있어서 현재시간을 명확하게 알 수 있게 해 줍니다.

 

함수 원형

struct tm * localtime(const time_t * _Time)

예제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS
#endif
 
#include <stdio.h>
#include <time.h>
 
void main()
{
    struct tm* pTimeInfo;
    time_t t = time(NULL);
    pTimeInfo = localtime(&t);
 
    int year = pTimeInfo->tm_year + 1900;    //연도에는 1900 더해줌
    int month = pTimeInfo->tm_mon + 1;    // 월에는 1 더해줌
    int day = pTimeInfo->tm_mday;
    int hour = pTimeInfo->tm_hour;
    int min = pTimeInfo->tm_min;
    int sec = pTimeInfo->tm_sec;
 
    printf("%d-%.2d-%.2d %.2d:%.2d:%.2d\n", year, month, day, hour, min, sec);
}
cs

asctime

tm 구조체를 읽어 들여서 Www Mmm dd hh:mm:ss yyyy 형식으로 스트링 리턴합니다.

함수 원형

char * asctime(_In_ const struct tm * _Tm);

예제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS
#endif
 
#include <stdio.h>
#include <time.h>
void main()
{
    struct tm* pTimeInfo;
    time_t t = time(NULL);
    pTimeInfo = localtime(&t);
 
    printf("%s", asctime(pTimeInfo));
}
cs

 

gmtime

time_t를 파라미터로 받아 UTC 시간을 리턴합니다.

함수 원형

struct tm * gmtime(const time_t * _Time)

예제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS
#endif
 
#include <stdio.h>
#include <time.h>
 
void main()
{
    struct tm* pTimeInfo;
    time_t t = time(NULL);
    pTimeInfo = gmtime(&t);
 
    int year = pTimeInfo->tm_year + 1900;    //연도에는 1900 더해줌
    int month = pTimeInfo->tm_mon + 1;    // 월에는 1 더해줌
    int day = pTimeInfo->tm_mday;
    int hour = pTimeInfo->tm_hour;
    int min = pTimeInfo->tm_min;
    int sec = pTimeInfo->tm_sec;
 
    printf("UTC %d-%.2d-%.2d %.2d:%.2d:%.2d\n", year, month, day, hour, min, sec);
    hour = hour + 9;
    if (hour >= 24)
    {
        day++;
        hour -= 24;
    }
        
    printf("KST %d-%.2d-%.2d %.2d:%.2d:%.2d\n", year, month, day, hour, min, sec);
}
cs

 

mktime

tm구조체를 이용해서 time_t타입으로 만듭니다.

함수 원형

time_t mktime(struct tm * _Tm)

예제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS
#endif
 
#include <stdio.h>
#include <time.h>
 
void main()
{
    struct tm* pTimeInfo;
    time_t t1 = time(NULL);
    pTimeInfo = localtime(&t1);
 
    pTimeInfo->tm_mday = pTimeInfo->tm_mday + 7;
 
    time_t t2 = mktime(pTimeInfo);
 
    printf("time : %d\n", t1);
    printf("7 days later %d\n", t2);
    printf("t2-t1 : %d\n", t2 - t1);
}
cs

현재 시간과 7일 후 시간의 차를 계산해보는 예제입니다.

1분을 초로 변환하면 60 

시간을 초로 변환하면 60 X 60 = 3,600

하루를 초로 변환하면 60 X 60 X 24= 86,400

7일을 초로 계산하면 60 X 60 X 24 X 7= 604,800

 

기타로 mktime함수를 실제로 구현해보았습니다.

특정 시간 time_t로 지정한 후 tm구조체에 넣은 후 다시 time_t로 변경해보았습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS
#endif
 
#include <stdio.h>
#include <time.h>
 
 
int GetMinToSec(int nMin);
int GetHourToSec(int nHour);
int GetDayToSec(int nDay);
int GetMonthToSec(int nMonth);
int GetYearToSec(int nYear);
void main()
{
    time_t t;
    struct tm* pTimeInfo;
    //t = time(NULL);
    t = 1620716583;
    pTimeInfo = localtime(&t);
 
    int year = pTimeInfo->tm_year + 1900;    //연도에는 1900 더해줌
    int month = pTimeInfo->tm_mon + 1;    // 월에는 1 더해줌
    int day = pTimeInfo->tm_mday;
    int hour = pTimeInfo->tm_hour;
    int min = pTimeInfo->tm_min;
    int sec = pTimeInfo->tm_sec;
 
    time_t nTime = sec + GetMinToSec(min) + GetHourToSec(hour - 9+ GetDayToSec(day) + GetMonthToSec(month - 1+ GetYearToSec(year - 1970);
 
    printf("%d-%.2d-%.2d %.2d:%.2d:%.2d\n", year, month, day, hour, min, sec);
    printf("%lld", nTime);
}
 
int GetMinToSec(int nMin)
{
    return nMin * 60;
}
int GetHourToSec(int nHour)
{
    return nHour * 3600;    // nHour * 60 * 60
}
int GetDayToSec(int nDay)
{
    return nDay * 86400;        // nDay * 60 * 60 * 24
}
int GetMonthToSec(int nMonth)
{
    int nMonthDay[12= { 312831303130313130313031 };    // Number of days per month
 
    int nMonthDayCount = 0;
 
    for (int i = 0; i < nMonth; i++)
        nMonthDayCount += nMonthDay[i];
 
    return nMonthDayCount * 86400;    // nMonthDayCount * 60 * 60 * 24
}
 
int GetYearToSec(int nYear)
{
    int nAddDay = nYear / 4;
 
    return  GetMonthToSec(12* nYear + nAddDay * 86400;    // GetMonthToSec(12) * nYear + nAddDay * 60 * 60 * 24
}
cs

지정한 1620716583과 같은 값이 출력되는 것을 확인할 수 있습니다.

반응형
반응형

안녕하세요.

오늘은 하이라이키(Hierarchy)에서 순위 변경하는 방법에 대해 알아보도록 하겠습니다.

UI 등 같은 위치에 겹치게 되면 하이라이키(Hierarchy)의 나중에 있는 게 더 위로 나옵니다.

물론 순서를 바꾸면, 순위를 바꿀 수 있습니다.

하지만, 실행 중에 변경하려면 스크립트를 이용해야 합니다.

스크립트에서 다음 함수를 사용할 수 있습니다.

Transform.SetAsLastSibling
해당 오브젝트의 순위를 마지막으로 변경(가장 나중에 출력되므로 겹쳐졋을 경우 앞으로 나옵니다.)

Transform.SetAsFirstSibling
해당 오브젝트의 순위를 처음으로 변경(가장 처음 출력되므로 겹쳐졋을 경우 가려집니다.)

Transform.SetSiblingIndex(int nIndex)
nIndex를 매개변수를 넣어서 순위를 지정합니다.(0이 처음입니다.)

Transform.GetSiblingIndex()
해당 오브젝트의 순위를 얻어옵니다.

 

예제

일단 이미지 오브젝트를 생성하고, blue, red로 지정하겠습니다.

그리고 스크립트가 들어갈 오브젝트를 하나 만듭니다.(ScriptObject)

스크립트에 다음과 같이 입력하면 현재 오브젝트의 순위값을 알 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class GameManger : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        GameObject goRed = GameObject.Find("Canvas/red");
        GameObject goBlue = GameObject.Find("Canvas/blue");
 
        Debug.Log("goRed : " + goRed.transform.GetSiblingIndex());
        Debug.Log("goBlue : " + goBlue.transform.GetSiblingIndex());
    }
}
 
cs

출력값은 다음과 같습니다.

빨간 사각형이 나중에 나오기 때문에 1로 표기 되었습니다.

순위를 변경할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class GameManger : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        GameObject goRed = GameObject.Find("Canvas/red");
        GameObject goBlue = GameObject.Find("Canvas/blue");
 
        Debug.Log("before goRed : " + goRed.transform.GetSiblingIndex());
        Debug.Log("before goBlue : " + goBlue.transform.GetSiblingIndex());
 
        goRed.transform.SetSiblingIndex(0);
        Debug.Log("after goRed : " + goRed.transform.GetSiblingIndex());
        Debug.Log("after goBlue : " + goBlue.transform.GetSiblingIndex());
    }
}
cs

출력값

16번째 줄 "goRed.transform.SetSiblingIndex(0);" 대신에 "goRed.transform.SetAsFirstSibling();", "goBlue.transform.SetAsLastSibling();"를 넣으면 같은 결과를 얻을 수 있습니다. 

 

반응형
반응형

안녕하세요.

오늘은 메세지박스에 WCHAR스트링으로 메세지 넣는것에 대해 알아보도록 하겠습니다.

일본어로 메세지박스의 메세지를 넣고 싶었는데, 프로젝트 자체가 이미 "멀티바이트 문자 집합 사용"으로 되어있어서,

일본어가 깨지는 문제가 발생했습니다.(물론 유니코드 문자 집합 사용을 하면 깨지지 않았습니다.)

하지만 이미 많이 진행된 프로젝트이기 때문에 유니코드 문자 집합으로 변경이 어려운 상황이였습니다.

그래서 WCHAR형으로 메세지를 넣으면 되지 않을까해서 찾아보던중,

MessageBoxW로 하면 된다는 걸 알게되었습니다.

WINAPI
MessageBoxW(
    __in_opt HWND hWnd,
    __in_opt LPCWSTR lpText,
    __in_opt LPCWSTR lpCaption,
    __in UINT uType);

lpText, lpCaption를 모두 WCHAR형으로 넣어주면 됩니다.

기본적으로 WINAPI함수입니다. 하지만 MFC에서도 hWnd을 0으로 사용하면 동일하게 사용할 수 있습니다.

예제
MessageBoxW(0, L"再試行してください。", L"Error", MB_OK );

반응형
반응형

안녕하세요.

오늘은 하나의 이미지를 자르는 방법에 대해 알아보도록 하겠습니다.

기본적인 ui image에서는 자르는게 안 되는 것 같습니다.(못 찾았습니다.)

Raw Image를 사용하면 자를 수 있습니다.

Hierarchy에서 UI - Raw Image로 오브젝트를 추가합니다.

Inspector에서 Raw Image 항목의 UV Rect 값을 변경하여 자를 수 있습니다.

X, Y에는 0~1 값이 들어가며, X, Y 자르는 시작 점을 지정할 수 있습니다.

W, H에는 0~1값이 들어가며, 너비와 높이를 지정할 수 있습니다.

좌표계는 다음과 같이 됩니다.

만약에 4등분으로 오른쪽 위를 반으로 자르고 싶다면 다음과 같이 설정하면 됩니다.

 

같은 방식으로 4등분에 X, Y값은 다음과 같이 설정할 수 있습니다.

1.  X : 0, Y : 0.5 (왼쪽 위)

2. X : 0.5, Y : 0.5 (오른쪽 위)

3. X : 0, Y : 0 (왼쪽 아래)

4 X : 0.5 Y 0 (오른쪽 아래)

 

반응형

+ Recent posts