http://tibyte.kr/178 에서 이어집니다.





드디어 달에 도착한 케르맨








다 쓴 연료을 발견하고 흥분을 감추지 못합니다.








또 다른 연료통 발견!









잠시 앉아서 휴식을 취합니다.











달의 아름다운 경관에 취해 정신을 놓아버린 케르맨.









지평선 너머에 지구가 보입니다.









이제 돌아갈 시간이 되어 우주선으로 다시 올라옵니다!

과연 무사히 갈 수 있을지...









아, 깜빡하고 깃발을 안 꽂고 왔네요!

내려서 깃발을 펼칩니다.






다시 우주선으로 점프해 올라와서

산적왕 히그마의 기운을 받아 탑!승!완!료!








지구를 향해 힘차게 연료를 분사합니다.








중력이 훨씬 약해서 쉽게 날아오르네요.







다시 우주공간을 헤쳐나갑니다.







어느새 멀어져서 작아진 달









적절히 연료를 분사하여 궤도를 형성합니다.








점점 가까워지고 있는 지구(케르빈)








마지막 연료통을 분리합니다!







진입 준비 완료!
우주인은 오랜 여행에 지쳤는지 미친듯이 웃고있군요.









으아아아 불탄다








고온을 잘 견뎌내고 낙하산 전개!
지금은 밤이네요..








바다에 착륙!










아침이 되어 우주선 밖으로 나가봅니다.

바다에 착륙했네요.







마침내 귀환 완료!











스팀에서 KSP 실행!!








처음으로 게임 내 위성인 뮌(Mun)에 가보겠습니다. 지구로 치면 달이죠.

힘차게 날아오르는 로켓









다 쓴 연료통 세게를 분리하고 가운데 로켓 점화!

둥근 모습의 지평선이 보이네요.

우측 하단에 입이 쭉 벌어진 비행사가 보입니다. 









게임 내 행성인 커빈의 대기권을 빠져나오고 있는 로켓.








드디어 우주가 보이기 시작하네요.

눈 앞에 펼쳐진 은하수를 보고 경이로움을 감추지 못하는 조종사...








연료 탱크 분리 후 다음 단에 있는 3개의 로켓을 다시 점화!

속도가 초속 2600m를 넘어섰네요.








태양전지판을 펼쳐서 전기를 충전합니다.









궤도시뮬레이터로 달에 접근하기 위한 궤도를 계산합니다.








지구로부터 유유히 멀어지고 있는 우주선...









달 주위를 도는 궤도를 만들고









궤도를 점점 좁혀가면서 달에 접근합니다.









저 멀리 지구가 보이네요.









달 착륙 시작!!










멀리서 본 우주선의 현재 위치와 궤도










로켓을 역분사해서 속도를 줄여줍니다.









속도가 초속 67m까지 줄어들었네요. 저 멀리 지구도 보입니다.









달 표면에 안착!

주변에서 뒹굴고 있는 부품 두개는 다 쓴 연료통입니다 ㅋ









우주선 밖으로 나와 달 표면에 내려봅시다.









드디어 달에 첫발을 딛는 순간











http://tibyte.kr/179 으로 이어집니다!














1페이지부터 5페이지까지 5개의 장면이 있고

다음 버튼과 이전 버튼이 있어서 페이지를 넘길 수 있는 프로그램을 만드는데

if문을 쓰지 않고 모듈러연산으로 처리하는 것이 생각나서 식을 구해보기로 했다.


아래 그림은 1부터 n까지의 자연수가 있을때

아무것도 더하지 않고 mod n연산을 하여 +1을 한 결과와,

처음에 1을 더한 경우, 2를 더한 경우에 대한 내용이다.


아래 그림은 처음에 n-2을 더한 경우와, n-1을 더한 경우이다. 

예를 들어 자연수의 집합 {1, 2, 3, 4, 5} 가 있을 때

집합의 각 원소에 n-2인 3을 더하면  집합은{4, 5, 6, 7, 8}가 되고 

이 집합의 mod 5를 구하면 {4, 0, 1, 2, 3} 이 되어

다시 이 집합에 +1을 하면 {5, 1, 2, 3, 4}가 되어서

최종적으로는 원래 집합이 우측으로 1칸씩 순환 이동하였다고 볼 수 있다.

또한 2는 1이 되고, 3은 2가 되고, 4는 3이 되고, 5는 4가 되고, 1은 다시 5가 되었으므로

처음에 만들고자 하였던 프로그램의 이전 버튼에 사용할 수 있을 것이다.


다시 위의 두 그림을 자세히 살펴보면,

집합의 원소 a가 있고 집합의 원소가 n개인 경우에, 이 집합의 원소를 p씩 증가시키고 싶을 때

(예를 들어 각각의 1,2,3,4,5 페이지에서 다음버튼을 눌러서 각각 2,3,4,5,1 페이지를 만들고자 하는 경우 등) 

다음과 같은 함수를 얻을 수 있다.

f(a,p,n) = (a+(p-1))% n +1

여기서 p는 좌측으로 p칸 순환시프트시킬 값도 된다.


처음의 프로그램 문제로 되돌아가서

5페이지일때의 다음버튼을 만든다고 하면 아래와 같이 프로그램을 작성할 수 있을 것이다.

page = (currentPage+(1-1))%5 + 1;

필요없는 연산을 제거하면,

page = currentPage%5 + 1;


이번에는 2페이지씩 넘기는 버튼을 만든다고 하면 코드는,

page = (currentPage+(2-1))%5 + 1; 에서 역시 필요없는 부분을 제거하여
page = (currentPage+1)%5 + 1;
이 된다.

그러면 반대방향으로 1페이지를 넘기는 이전버튼에 들어갈 코드는 어떻게 될까?
순환 시프트이므로 p값을 넣을 자리에 n-p값을 놓으면 된다.
page = (currentPage+(5-1-1))%5 + 1; 
page = (currentPage+3)%5 + 1; 


정리 :
(a+(p-1))% n +1
→ p씩 순환증가
→ p씩 좌측 순환시프트 

(a+(n-p-1))% n +1
→ p씩 순환감소
→ p씩 우측 순환시프트 



================================================================================






견본용 작품

(▼여기에 플래시가 첨부되어 있습니다)

화면을 클릭하면 탱크가 놓여집니다.

탱크 이미지 : fortress 2 blue














구성

1. 서비스 실행 시 알림 띄우기

2. 알림을 지워지지 않게 만들기('실행 중' 탭에 띄우기)




1장.


 안드로이드 이전 버전에서는 노티피케이션(Notification)을 띄우기 위해 Notification 인스턴스를 만들어 사용했지만

지금은 Notification.Builder를 통해 생성하도록 권장하고 있다.

여기서는 이전 버전과의 호환성을 위해 android.support.v4.app 패키지의 NotificationCompat.Builder 객체를 사용한다.


 그렇기 때문에 android-support-v4.jar 파일을 프로젝트에 추가해야 하는데

이 파일은 안드로이드sdk 설치 디렉토리 - extras - android - support - v4  경로에 있다.

이 파일을 이클립스 기준으로, 프로젝트속성 - java build path - libraries에 들어가 포함시키면 된다.




 서비스에서 노티피케이션을 띄우기 위한 코드는 서비스의 onStartCommand 함수 내에 작성한다.

먼저 NotificationCompat.Builder 객체를 생성하고, 필수로 SmallIcon과 ContentTitle, ContentText를 지정해 주어야 한다.


(▽ 상단바에 왼쪽편에 있는 아이콘들이 SmallIcon들이다.) 


(▽ "USB가 연결되었습니다"문구가 ContentTitle이고, "컴퓨터로부터 또는 컴퓨터로..."부분이 ContentText이다.) 



그리고 노티피케이션을 터치했을 때 어떤 동작을 할 것인가를 지정해 주는데

여기서는 메인 액티비티인 MainActivity를 실행해 주기로 하였다.


Intent를 만든 뒤 TaskStackBuilder 객체에 추가하고

그것을 바탕으로 TaskStackPendingIntent 객체를 완성한다.

그리고 그 객체를 처음에 만들었던 NotificationCompat.Builder 객체에서 setContentIntent()로 설정.

마지막으로 NotificationManager를 생성하여 notify()시키면 노티피케이션이 뜨게 된다.


코드는 아래와 같다. 


   NotificationCompat.Builder mBuilder =

    new NotificationCompat.Builder(this)

    .setSmallIcon(R.drawable.smallicon)

    .setContentTitle("알림바 실험")

    .setContentText("알림바입니다..");

   

   Intent resultIntent = new Intent(getApplicationContext(),MainActivity.class);

 

   TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);

   stackBuilder.addParentStack(MainActivity.class);

   stackBuilder.addNextIntent(resultIntent);

   

   PendingIntent resultPandingIntent =

    stackBuilder.getPendingIntent(

    0,

    PendingIntent.FLAG_UPDATE_CURRENT

    );

   

   mBuilder.setContentIntent(resultPandingIntent);

   

   NotificationManager mNotificationManager =

  (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);

 

   mNotificationManager.notify(3452, mBuilder.build());



 NotificationCompat.Builder 객체를 만들 때, 스타일을 설정하는 set메서드들이 NotificationCompat.Builder 반환값을 가지므로

.setSmaillIcon().setContentTitle().setContentText() 와 같이 이어지게 될 수 있다.


 (노티피케이션이 등록될 때 상단바에 메시지가 나타나는 것은 setTicker().로 설정할 수 있다.)

 

 MainActivity라고 되어있는 부분은 노티피케이션을 선택했을 때 실행할 액티비티이며 상황에 맞게 다른 액티비티 이름이 올 수 있다.


 notify()함수에서 3452는 노티피케이션을 관리하기 위한 id값으로 여기서는 임의의 값을 써 둔 것이다.



다른 컨텍스트에서 노티피케이션을 종료해야 할 때는 다시 NotificationManager를 생성하여

아까 지정한 id(3452)를 통해 지울 수 있다. 코드는 다음과 같다.


  NotificationManager mNotificationManager =

   (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);

 

  mNotificationManager.cancel(3452); 




2장. 


 안드로이드 운영체제는 시스템 자원이 부족하다든지 하는 경우에 실행 중인 서비스를 끌 수도 있다. (꺼졌다가 다시 켜지기도 한다)

이를 방지하기 위해선 서비스를 포그라운드(Foreground)로 실행해야 한다. 

또한 알림을 지워지지 않고 '실행 중'탭에 놓기 위해서도 역시 포그라운드로 서비스를 실행하면 된다.

서비스를 포그라운드로 실행하려면 노티피케이션을 띄워 주어야 하는데

1장에서 만든 NotificationCompat.Builder 객체를 그대로 사용할 수 있다.

코드는 아래와 같다. (위 코드와 중복되는 부분이 대부분)



    NotificationCompat.Builder mBuilder =

     new NotificationCompat.Builder(this)

     .setSmallIcon(R.drawable.smallicon)

     .setContentTitle("알림바 실험")

     .setContentText("알림바입니다..");

    

    Intent resultIntent = new Intent(getApplicationContext(),MainActivity.class);

  

    TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);

    stackBuilder.addParentStack(MainActivity.class);

    stackBuilder.addNextIntent(resultIntent);

    

    PendingIntent resultPandingIntent =

     stackBuilder.getPendingIntent(

     0,

     PendingIntent.FLAG_UPDATE_CURRENT

     );

    

    mBuilder.setContentIntent(resultPandingIntent);

    

   this.startForeground(3452,mBuilder.build());


1장의 코드와 비교하여 NotificationManager 부분이 없어지고 startForeground()함수를 추가됐다.


노티피케이션을 제거할 때는 아래 코드로 포그라운드를 취소하면 된다.


this.stopForeground(true);




 ====================================================================================================


전지의 기전력 측정 (8.56V)



데이터시트를 보면서 참고하기 위해 CdS cell의 저항값 측정



황화카드뮴 소자가 어두울수록 저항이 커지고 밝을수록 저항이 작아지는데

저항이 15㏀정도가 나오는 정도의 밝기를 점등/소등의 기준으로 하기로 했다.


NPN트랜지스터 2sc1815는 달링턴회로를 구성하여

베이스-이미터 전압이 1.2V가 될 때 활성화되도록 했다.


따라서 콜렉터에서 이미터로 전류가 쏟아져내려가서 LED가 점등될 때

CdS cell에도 1.2V의 전압이 걸리게 된다.

(회로가 두 방향으로 나눠질 때 전류는 나뉘어져 흘러가지만 전압은 동일하게 걸리므로..)

 

그러면 저항 R1에는 8.56-1.2 = 7.36V의 전압이 걸려야 한다.

회로에서 저항 R1과 CdS cell(일종의 가변저항)이 있는 부분을 보면

전압은 저항에 비례해서 걸리므로 1.2 : 15 =  7.36 : x 라는 비례식이 만들어진다(15는 위에서 기준을 잡은 CdS의 저항값)


x값을 계산해 보면 92㏀이 나온다.

여기서는 100㏀짜리 저항으로 회로를 구성했다.

그리고 트랜지스터의 콜렉터 쪽에 LED의 동작전류/전압에 맞는 적절한 저항 R2를 연결한다. 


아래와 같은 회로도가 완성되었다.





브레드보드에 부품들을 연결.





동작하는 모습을 촬영한 동영상.
















플래시디벨롭을 설치하고 한글을 쳐보니 네모가 마구 쏟아진다.

인코딩을 UTF-8로 하고 한글폰트를 써도 깨지길래

한시간가량 구글링을 하며 별 설정을 다 건드려봐도,

다른 버전으로 재설치를 계속 해봐도 여전히 깨져서 나오는 한글..

자포자기하는 심정으로 컴퓨터를 재부팅했는데 거짓말처럼 한글이 제대로 나오기 시작한다


***

윈도를 재설치하고 시스템 로켈을 한국어로 해보고 레지스트리 추가 등 별걸 다해봐도

불규칙적으로 영문폰트의 한글이 깨지는 현상이 계속 발생한다..

원인을 못찾아서 그 문제가 생기면 그냥 로그오프 후 로그온 중...






=======================================================================




1
2
3
4
5
6
7
8
9
10
11
12
13
14
var file:File = new File();
file.addEventListener(Event.SELECT, onSelected);
file.browseForSave("save");
 
function onSelected(evt:Event):void
{
    if (evt.target.extension != "bin")
        evt.target.nativePath += ".bin";
     
    var stream:FileStream = new FileStream();
    stream.open(evt.target as File, FileMode.WRITE);
    stream.writeUTF("Content");
    stream.close();
}


File 객체에서 browseForSave로 저장창을 열고

Event.SELECT이벤트가 발생했을 때 

파일의 확장자를 확인하여 원하는 확장자로 덧붙인다.


FileStream을 써야 하기 때문에 Adobe AIR로 해야한다.






*********************************************************




var urlLdr:URLLoader = new URLLoader();

try {

urlLdr.load(new URLRequest("http://tibyte.kr/tibyte.txt"));

} catch (e:IOError) {  //cannot catch

}


다음과 같이 존재하지 않는 파일을 로드하면 런타임 에러가 뜬다.

예외처리를 했는데도 catch가 되지 않는데, 에러가 비동기적으로 발생하기 때문.

그래서 이벤트를 통해 예외처리를 해야한다. 


var urlLdr:URLLoader = new URLLoader();

urlLdr.addEventListener(IOErrorEvent.IO_ERROR, function h(e:IOErrorEvent){});

urlLdr.load(new URLRequest("http://tibyte.kr/tibyte.png"));


 ※Loader클래스일때는 contentLoaderInfo속성에 IOErrorEvent를 걸어주면 된다. 










액션스크립트3.0으로 만든 RGB큐브.



(▼여기에 플래시가 삽입되어 있습니다)




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
import flash.display.Shape;
import flash.geom.Vector3D;
import flash.geom.Matrix3D;
import flash.events.MouseEvent;
 
var scale:int = 160;
var density:int = 10;
var densityCube:int = density*density*density;
var points:Vector.<Shape> = new Vector.<Shape>(densityCube);
var pointsPosition:Vector.<Vector3D> = new Vector.<Vector3D>(densityCube);
var centralPoint:Vector3D = new Vector3D(stage.stageWidth/2, stage.stageHeight/2, scale/2);
var angleSpd:Number = 0;
var mouseAngle:Number = 0;
var mouseDistance:Number = 0;
var mouseSin:Number = 0;
var mouseCos:Number = 1;
 
for(var i_z:int=0; i_z<density; i_z++) {
    for(var i_y:int=0; i_y<density; i_y++) {
        for(var i_x:int=0; i_x<density; i_x++) {
            var index:int = density*density*i_z+density*i_y+i_x;
            var color:uint = int(i_z/density*256)<<16|int(i_y/density*256)<<8|int(i_x/density*256);
            points[index] = new Shape();
            points[index].graphics.beginFill(color);
            points[index].graphics.drawRect(0,0,4,4);
            points[index].graphics.endFill();
             
            pointsPosition[index] = new Vector3D();
            pointsPosition[index].x = centralPoint.x-scale/2 + i_x*scale/(density-1);
            pointsPosition[index].y = centralPoint.y-scale/2 + i_y*scale/(density-1);
            pointsPosition[index].z = centralPoint.z-scale/2 + i_z*scale/(density-1);
             
            points[index].x = pointsPosition[index].x;
            points[index].y = pointsPosition[index].y;
            points[index].z = pointsPosition[index].z;
            addChild(points[index]);
        }
    }
}
 
 
stage.addEventListener(Event.ENTER_FRAME, enterFrameHandler);
function enterFrameHandler(evt:Event):void
{
    for(var index:int=0; index<densityCube; index++) {
        pointsPosition[index].x -= centralPoint.x;
        pointsPosition[index].y -= centralPoint.y;
        pointsPosition[index].z -= centralPoint.z;
         
        var old:Vector3D = new Vector3D(pointsPosition[index].x, pointsPosition[index].y, pointsPosition[index].z);
        pointsPosition[index].y = Math.cos(angleSpd*mouseSin)*old.y - Math.sin(angleSpd*mouseSin)*old.z;
        pointsPosition[index].z = Math.sin(angleSpd*mouseSin)*old.y + Math.cos(angleSpd*mouseSin)*old.z;
        old.x=pointsPosition[index].x; old.y=pointsPosition[index].y; old.z=pointsPosition[index].z;
        pointsPosition[index].x = Math.cos(angleSpd*mouseCos)*old.x - Math.sin(angleSpd*mouseCos)*old.z;
        pointsPosition[index].z = Math.sin(angleSpd*mouseCos)*old.x + Math.cos(angleSpd*mouseCos)*old.z;
     
        pointsPosition[index].x += centralPoint.x;
        pointsPosition[index].y += centralPoint.y;
        pointsPosition[index].z += centralPoint.z;
         
        points[index].x = pointsPosition[index].x;
        points[index].y = pointsPosition[index].y;
        points[index].z = pointsPosition[index].z;
    }
 
}
 
stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
function mouseMoveHandler(evt:MouseEvent):void
{
    var dx:Number = stage.mouseX-stage.stageWidth/2;
    var dy:Number = stage.mouseY-stage.stageHeight/2;
    mouseDistance = Math.sqrt(dx*dx+dy*dy);
    mouseCos = dx/mouseDistance;
    mouseSin = dy/mouseDistance;
    angleSpd = mouseDistance/2000;
}






===========================================================================================





+ Recent posts