MemoryStream ms = new MemoryStream();
			bmp.Save(ms, ImageFormat.Png);
			byte[] buf = ms.ToArray();

Bitmap을 Png형식으로 buf에 저장한다.





이미지 탭의 기본적인 기능을 완성하여 실제 게임데이터를 생성하여 단말기에 올려보았다.


클라이언트 엔진을 만드는 작업은 아니기 때문에 리소스 파일 그대로 올리고 각 이미지의 좌표만 정상적으로 출력이 되는지 확인




정상출력 ~ !







만들기 시작했을때 빨리 빨리 작업을 해야하는데... 계속하고 있는게 아니라 어디까지 했는지 기억도 안난다...



편의상 하나의 리스트(부모 및 자식 포함)를 '리소스'라고 명칭.

( 리소스, 통 이미지, 리스트, 통 파일 등등을 생각해보다가...)




추가 된 기능

 - 리소스 추가 및 삭제

 - 리소스에 이미지 추가 및 삭제

 - 리스트 더블 클릭 시 이름 변경 가능

 - 이미지 영역 처리(파란 선)

 - 리스트 아이템 선택 시 해당 이미지 선택 처리(알파값 있는 파란 배경)

 - 마우스로 이미지 위치 이동

 - (2013. 06. 13) 원하는 리소스만 저장 및 불러오기

 - (2013. 06. 16) 리소스 및 이미지 선택 시 오른쪽 Info 탭에 정보 표시

 - (2013. 06. 17) 단축키 추가

 - (2013. 06. 22) 이미지 추가 시 자동 정렬 (On/Off 가능)

 - (2013. 06. 23) 전체 데이터 저장 및 불러오기

 - (2013. 06. 23) 게임 데이터 생성(.res)

 - (2013. 06. 30) 리소스에 포함 된 총 이미지 수 표시

 - (2013. 06. 30) 현재 선택 된 이미지 Index 표시

 - (2013. 06. 30) 엔진에서 Index로 해당 이미지만 출력

 - (2013. 06. 30) 우측 TabControl 제거

 - (2013. 06. 30) 버그 수정

 - (2013. 07. 05) 키보드 방향키로 리스트 선택 가능

 - (2013. 07. 05) 우측 TabControl 제거

 - (2013. 07. 05) 선택된 이미지의 Index 및 Resource에 포함된 이미지 수 표시








커다란 메인 폼 안에 탭컨트롤을 사용하여 각각의 메뉴가 별도로 동작 가능하게 만들었다.

탭은 제작 예정인 이미지, 스프라이트, 맵, 데이터를 추가해 놓았고 추후 지속적으로 필요한 것이 있으면 추가 예정.





탭 안쪽의 왼쪽 공간은 리스트로 제작.

가운데 공간은 현재 작업이 보여지는 뷰 역할.

오른쪽 공간은 작업중인 데이터의 정보가 보여짐.


오른쪽 공간에 별도의 탭으로 제작한 이유는 만들다보면 무언가 지속적으로 추가 될 것 같아서...





엔진과 연동 가능한 툴을 먼저 제작하고 라이브러리를 제작한다.

완성까지 얼마나 걸릴지 모르는 1년 프로젝트~!


기본적인 기능 정하기.


[게임 툴]

 - C#으로 제작

 - 가공 가능한 데이터 : 이미지, 스프라이트, 맵, 데이터 등등

 - 데이터 보안



[게임 엔진]

 - 라이브러리 형태로 제작 (so 파일)

 - 안드로이드 버전 제작 하기

 - 툴에서 만들어진 데이터와 연동

 - OpenGL, OpenAL 사용





        try {
        	FileInputStream fin = new FileInputStream(fileName);
            ZipInputStream zin = new ZipInputStream(fin);

            byte[] buffer = new byte[1024];

            BufferedInputStream in = new BufferedInputStream(zin);
            
            ZipEntry ze = null;
            
            while ((ze = zin.getNextEntry()) != null) {
                
                FileOutputStream fout = new FileOutputStream(mContext.getFilesDir().getAbsolutePath()+"dir/" + ze.getName());
                BufferedOutputStream out = new BufferedOutputStream(fout);
                long c = 0;
                while ((c = in.read(buffer, 0, 1024)) != -1) {
                    out.write(buffer, 0, (int) c);
                }
                zin.closeEntry();
                out.close();
                fout.close();
            }
            
            in.close();
            zin.close();
        } catch (Exception e) {
        }
이렇게 하는겁니당 fileName에 zip의 상세경로를 넣어주고 dir에 원하는 폴더를 지정해주면 dir폴더에 압축파일이 풀리게 된다.
fileName예시 : 다운로드 받아 저장한 path/파일이름.zip









NDK 개발 시 앱이 종료되면서 위와같은 에러가 출력 될 때가 있다.

이때는 함수까지만 확인이 가능하고 정확한 라인을 알 수 없으므로 오류가 나는 라인을 알아 볼 수 있는 방법을 알아본다.


1. cygwin을 사용해서 cygwin이 설치 된 폴더에 bin으로 들어간다.


2. bin폴더에 오류가 발생한 라이브러리를 붙여넣기 한다. (lib라이브러리.so)


3. addr2line.exe -f -e 라이브러리명.so 00041f40

 위와 같은 형식의 명령어를 입력한다.

 00041f40은 위 이미지에 #06 pc 우측에 있는 16진수이다. 16진수를 입력하면 해당 함수의 몇번째 라인에서 오류가 났는지 출력 된다.


명령어 입력시 아래와같이 출력된다.


getJClass

C:/Develop/Android/workspace/프로젝트명/jni/jni-ndk.c:32


해당 프로젝트 jni-ndk.c 소스의 32번 라인에서 오류가 발생한걸 확인 할 수 있다.





ndk-build 시 'note: the mangling of 'va_list' has changed in GCC 4.4' 해당 문구가 뜨면


Android.mk 파일에 아래 문구를 추가한다.


LOCAL_CFLAGS :=  -Wno-psabi






#include <QApplication>
#include <QTreeWidget>
#include <QTreeWidgetItem>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QTreeWidget* treewidget = new QTreeWidget();

    QTreeWidgetItem* header = new QTreeWidgetItem;
    header->setText(0, "name");
    header->setText(1, "age");
    header->setText(2, "etc");
    treewidget->setHeaderItem(header);

    QTreeWidgetItem* row1 = new QTreeWidgetItem(treewidget);
    row1->setText(0, "col 1");
    row1->setText(1, "col 2");
    row1->setText(2, "col 3");

    QTreeWidgetItem* row2 = new QTreeWidgetItem;
    treewidget->addTopLevelItem(row2);
    row2->setText(0, "?");
    row2->setText(1, "??");
    row2->setText(2, "???");

    QTreeWidgetItem* subItem1 = new QTreeWidgetItem;
    subItem1->setText(0, "? - 1");
    subItem1->setText(1, "? - 2");
    subItem1->setText(2, "? - 3");
    row2->addChild(subItem1);

    QTreeWidgetItem* subItem2 = new QTreeWidgetItem;
    subItem2->setText(0, "? - 1");
    subItem2->setText(1, "? - 2");
    row2->addChild(subItem2);

    treewidget->resize(500, 500);
    treewidget->setSortingEnabled(TRUE);
    treewidget->show();

    return app.exec();
}

이번에는 TreeWidget을 만들어 보도록 하겠습니다.


02 ~ 03. 필요한 헤더파일을 추가시킵니다.

09. QTreeWidget 객체를 생성합니다.

11. QTreeWidgetItem 객체를 생성하고

12 ~ 14. 생성한 item 객체에 3개의 column을 만들었습니다.

15.  treewidget 객체에 방금 생성한 item 객체를 헤더로 추가합니다.


여기까지 최상단 목록을 만들었습니다.


17. QTreeWidgetItem 객체를 생성 합니다. item 객체를 생성하는데에는 두가지 방법이 있는것 같은데요. 17, 22 ~ 23. 라인을 보시면 조금은 다르게 되어있지요.

자세한건 모르겠지만 두개가 같은 결과를 얻습니다.

18 ~ 20. 생성한 item에 3개의 column을 만들었습니다.


여기까지 행을 만드는 방법입니다.


이번에는 하나의 행에 속해있는 또 하나의 행(서브)을 만들어 보도록 하겠습니다.


28 ~ 31. 위와 같은 방법으로 item 객체를 생성하고 column을 추가합니다.

32. 해당 item 객체를 위에서 만든 row2 객체의 자식으로 추가합니다.

34 ~ 37. 여러개의 자식을 추가시킬 수 있습니다.


40. 해당 옵션을 사용하면 최상위 목록을 클릭했을때 자동정렬이 되도록 할 수 있습니다.








릴리즈 버전으로 빌드 후 exe 파일을 실행시켰을때 dll이 없다는 에러가 나오면 해당하는 dll을 프로젝트에 포함시키면 됩니다.


하지만 dll을 포함하였음에도 '프로시저 시작 지점 ?...' 이라는 에러나 발생할때는 qt\bin안에 있는 dll이 아닌


qt\qtcreator\bin 안에 있는 dll을 포함시켜주니 정상 작동합니다.



'QT' 카테고리의 다른 글

QT 설치하기  (0) 2013.09.08
[ Qt ] QTreeWidget 만들기  (0) 2012.10.02
[ Qt ] 프로그램 배포시 에러 ( 프로시저 시작 지점 ... )  (0) 2012.10.02
[ Qt ] 로그 출력 ( print console )  (0) 2012.10.01
[ Qt ] 시그널 / 슬롯  (0) 2012.10.01
[ Qt ] 메뉴바 생성하기  (0) 2012.10.01



qDebug() << " 블라블라 ~ ";






이 전 메뉴바 만들기에서 시그널과 슬롯을 사용해 보았습니다.

슬롯은 시그널과 연결될 수 있고 시그널과 연결된 슬롯은 해당 시그널이 발생될 때마다 자동으로 호출됩니다.

시그널/슬롯 매커니즘은 QObject에 구현되어 있으 QObject를 상속받는 클래스라면 어디에서든 사용이 가능합니다.


connect(sender, SIGNAL(signal), receiver, SLOT(slot));


sender와 receiver는 QObject에 대한 포인터이며 signal과 slot은 매개변수의 이름이 생략된 함수 시그너처 입니다.

SIGNAL()과 SLOT() 매크로는 자신의 인자를 문자열로 변환하는 역할을 담당합니다.



기본적으로 시그널과 슬롯은 아래와 같이 연결될 수 있습니다.


- 하나의 시그널이 여러 개의 슬롯에 연결될 수 있다.

connect(sender, SIGNAL(signal), receiver, SLOT(slot1));

connect(sender, SIGNAL(signal), receiver, SLOT(slot2));


- 여러 개의 시그널이 하나의 슬롯에 연결될 수 있다.

connect(sender, SIGNAL(signal1), receiver, SLOT(slot));

connect(sender, SIGNAL(signal2), receiver, SLOT(slot));


- 시그널은 또 다른 시그널에 연결될 수 있다.

connect(sender, SIGNAL(signal), receiver, SIGNAL(signal));

- 기존에 설정된 연결은 해제될 수 있다. ( Qt는 객체가 삭제될 때 그 객체가 갖고 있던 모든 연결을 해제합니다. )

disconnect(sender, SIGNAL(signal), receiver, SLOT(slot));

또한, 시그널과 슬롯이 정상적으로 연결되려면 매개변수 타입과 순서가 동일해야 합니다.

connect(sender, SIGNAL(signal_test(int, const QString &)), receiver, SLOT(slot_test(int, const QString &));

하지만 시그널이 슬롯보다 더 많은 매개변수를 갖는 경우에는 예외적으로 연결되 가능하며, 시그널이 갖는 추가적인 매개변수는 무시 됩니다.

connect(sender, SIGNAL(signal_test(int, const QString &)), receiver, SLOT(slot_test(int));






이번에는 상단 메뉴바를 생성하는 예제 입니다.


main.cpp

#include 
#include <qtextcodec>
#include "mainwindow.h"

int main(int argc, char **argv)
{
    QApplication app(argc, argv);

    QTextCodec::setCodecForTr(QTextCodec::codecForName("eucKR"));

    MainWindow *mainWindow = new MainWindow();
    mainWindow->setFixedSize(500, 400);
    mainWindow->show();

    return app.exec();
}

한글 출력을 위해 코덱을 설정해 주었으며 mainwindow를 사용자가 창을 조절하지 못하도록 생성하였습니다.



mainwindow.h


#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <qmainwindow>

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow();

private:

private slots:
    // 파일
    void Main_SlotNewFile();

    // 테스트
    void Main_SlotTest1();
    void Main_SlotTest2();
    void Main_SlotTest3();
};
#endif // MAINWINDOW_H
mainwindow의 헤더파일 입니다.

QMainWindow를 상속받고 있습니다.


마지막 부분을보면 slots이라는 키워드가 있습니다.

이 슬롯은 다음에 설명하기로 하겠습니다.



mainwindow.c 소스 파일로 넘어갑니다.


#include <QMenuBar>
#include <QStatusBar>
#include <QAction>
#include "mainwindow.h"

MainWindow::MainWindow()
{
    QMenu *pFileMenu;
    QStatusBar *pStartusbar;

    // 파일
    QAction *pSlotNewFile = new QAction(tr("새 파일"), this);
    pSlotNewFile->setShortcut(tr("Ctrl+N"));
    pSlotNewFile->setStatusTip(tr("새 파일을 생성합니다."));
    connect(pSlotNewFile, SIGNAL(triggered()), this, SLOT(Main_SlotNewFile()));

    pFileMenu = menuBar()->addMenu(tr("파일"));
    pFileMenu->addAction(pSlotNewFile);

    // 테스트
    QAction *pSlotTest1 = new QAction(tr("테스트 1"), this);
    pSlotTest1->setShortcut(tr("Ctrl+T"));
    pSlotTest1->setStatusTip(tr("1번 메뉴 테스트"));
    connect(pSlotTest1, SIGNAL(triggered()), this, SLOT(Main_SlotTest1()));

    QAction *pSlotTest2 = new QAction(tr("테스트 2"), this);
    pSlotTest2->setStatusTip(tr("2번 메뉴 테스트"));
    connect(pSlotTest2, SIGNAL(triggered()), this, SLOT(Main_SlotTest2()));

    QAction *pSlotTest3 = new QAction(tr("테스트 3"), this);
    pSlotTest3->setStatusTip(tr("3번 메뉴 테스트"));
    connect(pSlotTest3, SIGNAL(triggered()), this, SLOT(Main_SlotTest3()));

    pFileMenu = menuBar()->addMenu(tr("테스트"));
    pFileMenu->addAction(pSlotTest1);
    pFileMenu->addAction(pSlotTest2);
    pFileMenu->addAction(pSlotTest3);

    // 상태바 연결
    pStartusbar = statusBar();
}

void MainWindow::Main_SlotNewFile()
{

}

// 이미지
void MainWindow::Main_SlotTest1()
{

}
void MainWindow::Main_SlotTest2()
{

}
void MainWindow::Main_SlotTest3()
{

}

메뉴바 생성을위한 몇가지 헤더파일이 추가되어있습니다.

08 ~ 09. 라인에서 QMenu와 QStatusBar를 선언하였습니다. QMenu는 만들고자 하는 메뉴바, QStatusBar는 화면 하단 상태바 입니다. 
12. 라인에서 QAction을 생성하고 있습니다. QAction은 큰 메뉴안에 들어가는 작은 메뉴입니다.
13. 라인에서는 단축키를 지정해주고 있습니다. Ctrl+N을 누르면 '새 파일'이 실행되겠지요.
14. 라인에서는 상태바에 출력될 팁을 작성하고 있습니다. '새 파일'에 마우스를 올려놓으면 하단 상태바에 해당 문구가 출력 됩니다.
15. 라인은 시그널과 슬롯을 연결해 주고 있는데요, 아직 시그널과 슬롯을 다루지 않았으므로 간단하게 설명하겠습니다.

connect(pSlotNewFile, SIGNAL(triggered()), this, SLOT(Main_SlotNewFile()));
pSlotNewFile 이 SIGNAL(triggered())되면 this의 SLOT(Main_SlotNewFile())을 실행시켜라.

시그널과 슬롯의 관계를 알지 못해도 어떠한 방식으로 사용되는지 아실 수 있으실 겁니다.

17 ~ 18. 라인은 상위 메뉴바에 '파일' 이라는 큰 메뉴를 생성하고 지금까지 만든 '새 파일' 액션을 추가시켜 줍니다.

21 ~ 37. '새 파일' 액션을 만든 것 처럼 나머지 메뉴도 생성하여 줍니다.

40. QStatusBar에 상태바를 연결하여 줍니다.


빌드 후 실행해보면 아래와같은 프로그램이 실행이 됩니다.





위 스크린샷을 보시면 지금까지 작업한 것들이 정상적으로 출력되고 있습니다.

1. 파일과 테스트라는 큰 메뉴가 존재하고

2. 생성한 액션만큼 하위 메뉴가 만들어졌으며

3. 단축키를 지정한 액션에 단축키가 등록되어 있습니다.

4. 화면 하단에 상태바도 정상적으로 작동 하는군요.


:)






창이 움직이지 않아야 하는 프로그램이라면 창의 사이즈를 입력시킬때


setFixedSize(width, height)로 입력을해주면 사용자가 창 크기를 변경 시킬 수 없습니다.






텍스트를 출력할때 tr("한글")을 사용하면 이상한 문자로 출력될때 해결방법 입니다.


간단하게 main에

#include <QTextCodec>

int main(..

QTextCodec::setCodecForTr(QTextCodec::codecForName("eucKR"));

..
}



헤더 하나와 소스 한줄을 추가하고 실행하면 한글이 정상적으로 출력됩니다.





이 전 글에서 제작한 정적 라이브러리를 원하는 프로젝트에 포함시키는 방법입니다.


1. 먼저 준비된 라이브러리 파일(.a)과 헤더파일(.h)을 jni 폴더에 복사합니다.


2. Android.mk 파일을 열어 아래와 같이 수정합니다.



LOCAL_PATH := $(call my-dir)

# 미리 준비한 라이브러리 파일 관련
include $(CLEAR_VARS)
LOCAL_MODULE := static_berabue			# 대충 적으시면 됩니다.
LOCAL_SRC_FILES = ./libberabue.a		# 준비된 라이브러리 파일명
include $(PREBUILT_STATIC_LIBRARY)


include $(CLEAR_VARS)

# 현재 프로젝트 관련
# 제작될 so 파일명
LOCAL_MODULE := curlib

# 현재 프로젝트에 사용중인 소스파일명
LOCAL_SRC_FILES := jni-ndk.c\
				GLGameRenderer.c\

# 사용하는 라이브러리가 있다면.. 없으면 제거
LOCAL_LDLIBS := -lGLESv1_CM\
				-llog\
				-ljnigraphics\

# 위에서 적은 모듈명
LOCAL_STATIC_LIBRARIES := static_berabue

include $(BUILD_SHARED_LIBRARY)

작성이 완료되었으면 ndk-build를 해봅니다.


오류가 없다면 so 파일이 생성되어있습니다.







NDK로 개발중 자주 사용하는 부분만 따로 라이브러리화 시키기 위해서 정적 라이브러리 파일 .a를 만드는 방법입니다.


라이브러리화 시킬 소스파일이 있는 폴더에 android.mk 파일을 아래와 같이 작성합니다.

LOCAL_PATH := $(call my-dir)

# 제작될 라이브러리(.a) 소스
include $(CLEAR_VARS)

# 라이브러리 이름
LOCAL_MODULE    := berabue

# 라이브러리에 포함될 소스 파일명
LOCAL_SRC_FILES := main.c\
				math.c\
				struct.c\

include $(BUILD_STATIC_LIBRARY)


# 쓰레기 .so 파일로 필수로 작성되어야 합니다
include $(CLEAR_VARS)

# 아무것이나 적습니다
LOCAL_MODULE    := garbege

# 위에 작성한 정적 라이브러리 이름
LOCAL_STATIC_LIBRARIES := berabue
include $(BUILD_SHARED_LIBRARY)

android.mk 파일을 위와같이 작성한 뒤 해당 폴더를 빌드(ndk-build)하


obj -> local -> armeabi 폴더에 berabue.a 파일이 생성됩니다.


해당 라이브러리파일과 소스에 사용되는 헤더파일(.h)을 같이 사용할 프로젝트에 넣으면 됩니다.










프로젝트를 Import할때 Invalid project description.  overlaps the location of another project: 에러가 나타난다면


Android -> Existing Android Code Into Workspace가 아닌


General -> Existing Projects into Workspace를 선택하여 Import하면 에러없이 된다.








android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application


다이얼로그 생성시 getApplicationContext(); 을 사용하여 발생된다.


액티비티명.this로 해결가능.






1. 구글 개발자 사이트에서 맥os용 NDK를 다운받아 원하는곳에 압축을 풀어 놓는다.


2. 터미널을 켜고 ls -al을 입력하여 .bash_profile 파일이 있는지 확인한다. (없을경우로 설명, 있으면 지우고(?))


3. vi .bash_profile을 입력하고 i 를 입력하여 입력모드로 변경


4. export PATH=${PATH}:/Users/ChangKeun/Program/Android/android-ndk/ (ndk폴더가 있는 경로 입력)


5. esc를 눌러서 입력모드 종료 후 : 를 입력시키고 wq 입력 후 ls -al 생성되어있음.


6. source .bash_profile 입력을 통하여 path설정


7. ndk-build 테스트 ~ 성공 ~ !



'Android > Etc.' 카테고리의 다른 글

프로젝트에 정적 라이브러리 추가하기  (0) 2012.09.25
정적 라이브러리 만들기  (0) 2012.09.25
Max OS에서 NDK 개발하기  (1) 2012.07.04
넥서스S USB 드라이버 다운  (0) 2011.04.18
Orientation 고정, 키보드 숨기기  (0) 2011.04.17
멀티터치 구현하기  (0) 2011.03.16
  1. 라베인 2012.07.19 17:38 신고

    내가 한건 저 .bash_profile에 adb경로도 추가해줬고,

    이클립스에 Builder에서 새로 만들어서 ndk-build를 연결시켜줬음 ~ 그러니깐 이클립스 Console탭에서 컴파일뜨드라고~

+ Recent posts