import pygame
from pygame.locals import *
from Box2D import *
from Box2D.b2 import *

SCREEN_WD = 640
SCREEN_HT = 480
TARGET_FPS = 60
PPM = 20.0

screen = pygame.display.set_mode((SCREEN_WD, SCREEN_HT), 0, 32)
pygame.display.set_caption("PyBox2D_Example")
clock = pygame.time.Clock()

world = b2World(gravity = (0, -10), doSleep = True)

ground1BodyDef = b2BodyDef()
ground1BodyDef.position.Set(0, 0)
ground1Body = world.CreateBody(ground1BodyDef)
ground1Shape = b2PolygonShape()
ground1Shape.SetAsBox(50, 1)
ground1Body.CreateFixture(shape = ground1Shape)

box1BodyDef = b2BodyDef()
box1BodyDef.type = b2_dynamicBody
box1BodyDef.position.Set(20, 45)
box1BodyDef.angle = 15
box1Body = world.CreateBody(box1BodyDef)
box1Shape = b2PolygonShape()
box1Shape.SetAsBox(2, 1)
box1FixtureDef = b2FixtureDef()
box1FixtureDef.shape = box1Shape
box1FixtureDef.density = 1
box1FixtureDef.friction = 0.3
box1FixtureDef.filter.categoryBits = 0x0002
box1FixtureDef.filter.maskBits = 0x0002 | 0x0001
box1Body.CreateFixture(box1FixtureDef)

circle1BodyDef = b2BodyDef()
circle1BodyDef.type = b2_dynamicBody
circle1BodyDef.position.Set(20, 0.5)
circle1Body = world.CreateBody(circle1BodyDef)
circle1Shape = b2CircleShape()
circle1Shape.radius = 0.5
circle1FixtureDef = b2FixtureDef()
circle1FixtureDef.shape = circle1Shape
circle1FixtureDef.density = 1
circle1FixtureDef.friction = 0.3
circle1FixtureDef.filter.categoryBits = 0x0004
circle1FixtureDef.filter.maskBits = 0x0004 | 0x0001
circle1Body.CreateFixture(circle1FixtureDef)

class MyContactListener(b2ContactListener):
    def BeginContact(self, contact):
        print("BeginContact")
    def EndContact(self, contact):
        print("EndContact")

contactListener = MyContactListener()
world.contactListener = contactListener

timeStep = 1.0 / 60
velIters = 10
posIters = 10

colors = {
    staticBody  : (255,255,255,255),
    dynamicBody : (127,127,127,255),
}

def my_draw_polygon(polygon, body, fixture):
    vertices=[(body.transform*v)*PPM for v in polygon.vertices]
    vertices=[(v[0], SCREEN_HT-v[1]) for v in vertices]
    pygame.draw.polygon(screen, colors[body.type], vertices)
polygonShape.draw=my_draw_polygon

def my_draw_circle(circle, body, fixture):
    position=body.transform*circle.pos*PPM
    position=(position[0], SCREEN_HT-position[1])
    pygame.draw.circle(screen, colors[body.type], [int(x) for x in position], int(circle.radius*PPM))
circleShape.draw=my_draw_circle

running = True
while running:
    for event in pygame.event.get():
        if event.type == QUIT:
            running = False
            continue
        if event.type == KEYDOWN and event.key == K_ESCAPE:
            running = False
            continue

    screen.fill((0, 0, 0, 0))

    for body in world.bodies:
        for fixture in body.fixtures:
            fixture.shape.draw(body, fixture)
                     
    world.Step(timeStep, velIters, posIters)
    pygame.display.flip()
    clock.tick(TARGET_FPS)

pygame.quit()
print("done")


[Python] pyBox2D 사용 예제 - 6. 충돌 필터링


충돌 필터링이란 Fixture들을 카테고리로 분류하여 카테고리간에 충돌이 일어나게 하거나 일어나지 않도록 제어하는 것을 말합니다.


위의 예제 코드에서, static ground body인 지면 위로, dynamic body인 사각형과 원모양이 떨어지는데, 사각형의 충돌 필터는 다음과 같이 정의되어 있습니다.


box1FixtureDef.filter.categoryBits = 0x0002
box1FixtureDef.filter.maskBits = 0x0002 | 0x0001


사각형의 카테고리(categoryBits)를 0x0002로 하며, 상대방의 카테고리가 0x0002나 0x0001 인 경우에만 충돌을 일으킨다는 의미입니다.


원의 충돌 필터는 다음과 같이 정의되어 있습니다.


circle1FixtureDef.filter.categoryBits = 0x0004
circle1FixtureDef.filter.maskBits = 0x0004 | 0x0001


원의 카테고리를 0x0004로 하며, 상대방의 카테고리가 0x0004나 0x0001 인 경우에만 충돌을 일으킨다는 의미입니다.


따라서 이 상태에서 사각형과 원은 서로 충돌하지 않습니다. 다만 categoryBit의 기본초기값은 0x0001이며, static ground body의 categoryBit는 정의되지 않아서 기본초기값을 가지므로 사각형과 원은 지면과는 충돌하여 멈추게 됩니다.


만약 maskBits에서 | 0x0001을 빼버리면 지면과도 충돌하지 않고 통과되어 지나가는 것을 볼 수 있습니다.


사각형과 원을 충돌하게 하려면, 사각형의 maskBits를 0x0004 | 0x0001로 하고, 원의 maskBits를 0x0002 | 0x0001로 하면 됩니다. 한쪽이 아닌 양쪽 모두에 설정되어 있어야 충돌이 일어납니다.


box1FixtureDef.filter.maskBits = 0x0004 | 0x0001

...

circle1FixtureDef.filter.maskBits = 0x0002 | 0x0001


그리고 maskBits를 설정하지 않았을 때, 기본값은 0xFFFF인데, 이는 모든 카테고리와 충돌을 일으킨다는 의미입니다.



다음은 contact listener를 설정하는 코드입니다.


class MyContactListener(b2ContactListener):
    def BeginContact(self, contact):
        print("BeginContact")
    def EndContact(self, contact):
        print("EndContact")

contactListener = MyContactListener()
world.contactListener = contactListener


Contact listener를 설정하게 되면, 어떤 충돌이 시작되고 끝날 때에 해당 call-back 함수가 호출되어 적당한 동작을 실행할 수 있게 해줍니다. 충돌이 시작될 때, BeginContact 함수가, 충돌이 끝났을 때, EndContact 함수가 호출됩니다.



,

1. 전역 함수 정의하기


-> app.js 파일 안에서 정의한다.


function myGlobalFunction() {

}



2. 로딩 완료시에 함수 호출하기


-> app.js 파일 안에서 정의한다.


document.addEventListener("deviceready", onDeviceReady, false);

function onDeviceReady() {

}



3. 스플래시 화면 변경하기


-> index.html 파일안에 css로 되어있는 부분을 수정한다.



4. 로그 출력하기


console.log('Print this');



5. 팝업 메세지 출력하기


alert("Hello");



6. 한글 출력 문제 해결


-> 한글이 제대로 출력되지 않을 경우, UTF-8(BOM 없음)으로 저장하여야 한다 (Notepad++ 등 사용)



7. View 배경 색상 변경하기


-> View 정의 시에 style로 지정한다.


Ext.define('projectname.view.ViewName', {

    extend: 'Ext.Container'
    ,config: {
        layout: { type: 'default' }
        ,style: 'background-color: #0000FF'

        ...


혹은 css로 지정




테스트한 버전 : 2.3.1

,
프로퍼티 쓰기:

window.localStorage.setItem(key, value);



프로퍼티 읽기:


var value = window.localStorage.getItem(key);




* 테스트한 버전 : 2.9.0

,

Textfield가 아래와 같이 정의되어있을 경우,


{
    xtype: 'textfield'
    ,id: 'myTextField'
}



입력값 읽기:


Ext.getCmp('myTextField').getValue();



입력값 쓰기:


Ext.getCmp('myTextField').setValue(size);




테스트한 버전 : 2.3.1

,

.my-transparent-button {
    background-color: transparent !important;
    background-image: none !important;
    border-color: transparent;
    border: none;

    color: #FFFFFF;
}



버튼에 적용하기 :


{
    xtype: 'button'
    ,text: 'my button'
    ,cls: 'my-transparent-button'
    ,baseCls: 'my-transparent-button'
    ,pressedCls: 'my-transparent-button-pressed'
}


my-transparent-button-pressed는 버튼이 눌렸을 때 적용되며 color 속성값을 다르게 해서 만들어준다.




* 테스트한 버전 : Sencha Touch 2.3.1

,

1. 개인 css 파일을 다음 경로에 생성한다.


resources/css/my_css.css


2. app.json 파일에 개인 css 파일을 등록한다.


    "css": [
        {
            "path": "resources/css/app.css",
            "update": "delta"
        },
        {
            "path": "resources/css/my_css.css",
            "update": "delta"
        }
    ],




* 적용 버전 : Sencha Touch 2.3.1

,

안드로이드용으로 작업한 내용으로 IOS용 빌드를 하는 방법에 대해서 간단히 알아보겠습니다.



1. <PhoneGap 설치 폴더>/lib/ios/bin 밑에서 다음의 내용을 실행하면 마찬가지로 Xcode 프로젝트가 생성되어 Xcode에서 불러들일 수 있습니다. (MacOS에서 실행)


./create <프로젝트가 생성될 폴더> <패키지명> <프로젝트명>



2. 생성된 프로젝트 폴더 밑에 보면 www 폴더가 있는데, 이 안에 있는 cordova.js 파일을 임의의 위치에 복사한 후, www 폴더를 삭제합니다.


3. 안드로이드용으로 작업했었던, senchapj\build\package 혹은 senchapj\build\testing 밑에 있는 폴더를, 마찬가지로 위 2번의 www 위치에 복사한 후 폴더명을 www로 바꾸면 되는데, 주의할 점은 cordova.js 파일이 안드로이드용과 IOS용이 따로 있기 때문에 각각 적합한 것을 넣어줘야 한다는 점입니다. 따라서 2번에서 복사해두었던 cordova.js 파일을 www 안에 덮어써주어야 합니다.


4. Xcode 상에서 빌드 및 실행을 하여 하이브리드 앱의 작동을 확인합니다.


,

Sencha Touch 프레임웍을 사용한 화면이 나타나는 것은 확인했으니, 다음은 Phone Gap의 native API도 정상 동작하는지 확인해보겠습니다.



1. senchapj/app/view 밑에 있는 Main.js 파일을 열고, html: [ ... ] 블럭의 맨 앞에 다음과 같이 추가해줍니다.


html: [

"<a href=\"javascript:navigator.notification.alert('Hello!')\">Click me</a>",

...

]


2. 빌드 후, Click 텍스트를 누를 때, 팝업이 나타나는 것을 확인합니다.



,