2013-09-26 126 views
20

Tôi tự hỏi liệu có một công cụ có thể được sử dụng để tạo ra các cơ quan vật lý phức tạp dễ dàng trong SpriteKit hay không. Tôi muốn có một khối lượng cơ thể vật lý dựa trên hình dạng đa giác. SpriteKit cho phép để tạo ra các cơ quan như vậy với phương pháp đó:SKPhysicsBody của SpriteKit với công cụ trợ giúp đa giác

+ (SKPhysicsBody *)bodyWithPolygonFromPath:(CGPathRef)path 

Đáng tiếc là nó tốn thời gian công việc để tạo ra những con đường như vậy bằng tay, và nó có thể là có vấn đề khi thử nghiệm. Có một ứng dụng SpriteHelper cho phép bạn xác định hình dạng cơ thể trong trình chỉnh sửa trực quan dễ sử dụng, nhưng ứng dụng này không thể xuất đường dẫn có thể được sử dụng tại đây. Nó được làm cho cocos2d và nó có rất nhiều thứ như kết cấu đóng gói vv mà tôi không cần và tôi không thể sử dụng với SpriteKit. Có ai biết một giải pháp mà sẽ cho phép để xác định CGPath của một cách dễ dàng hoặc thậm chí có thể tự động tạo ra chúng từ hình ảnh png với kênh alpha? Mặc dù tính năng tạo tự động từ trải nghiệm của tôi sẽ cần tối ưu hóa, vì các hình dạng cơ thể phải đơn giản nhất có thể khi họa tiết có thể có hình dạng phức tạp hơn.

+0

PhysicsEditor sẽ nhận được một bản cập nhật Kit Sprite sớm. – LearnCocos2D

+0

@ LearnCocos2D Tôi chắc chắn sẽ mua nó khi hỗ trợ SpriteKit sẽ được thêm vào. Tôi hy vọng sẽ có tùy chọn để xuất hình dạng va chạm trong định dạng mã Objective-c (khai báo CGPath hoặc một cái gì đó tương tự). Việc xuất sang định dạng tệp tùy chỉnh được đọc bởi thư viện bên ngoài không phải là những gì tôi đang tìm kiếm. – Darrarski

+0

xuất mã là một ý tưởng thực sự tồi vì nó dễ dàng phá vỡ, các công cụ tốt luôn ghi vào định dạng tệp tùy chỉnh (thường là xml) và sau đó cung cấp mã trình tải – LearnCocos2D

Trả lời

64

Tôi đang tìm chính xác điều tương tự, vì nó bật ra tôi đã làm một ứng dụng web nhỏ cho mục đích này.

SKPhysicsBody Đường dẫn Generator

như hành động trong ví dụ: enter image description here

Cập nhật 2015/02/13: kịch bản

<!DOCTYPE html> 
 
<html> 
 
    <head> 
 
     <meta charset="UTF-8"> 
 
     <title>SpriteKit Tools - SKPhysicsBody Path Generator</title> 
 
     <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css"> 
 

 
     <style> 
 
      /* disable responsive */ 
 
      .container { 
 
       max-width: none; 
 
       width: 970px; 
 
      } 
 
      #sprite { 
 
       background-color: #eee; 
 
       position: absolute; 
 
      } 
 
      #path { 
 
       cursor: crosshair; 
 
       opacity: 0.5; 
 
      } 
 
     </style> 
 

 
    </head> 
 
    <body> 
 
     <div class="container"> 
 
      <h1>SKPhysicsBody Path Generator</h1> 
 
      <p class="lead">Want to use [SKPhysicsBody bodyWithPolygonFromPath:path] easier way like me? Here with a small helper for easier path drawing, hope it help others too.</p> 
 
      <div class="row"> 
 
       <div class="col-md-6"> 
 
        <h5>Basic Instruction</h5> 
 
        <ol> 
 
         <li><small>Drag and drop the sprite image into drop zone.</small></li> 
 
         <li><small>Start drawing path by clicking on coordinates.</small></li> 
 
        </ol> 
 
       </div> 
 
       <div class="col-md-6"> 
 
        <h5>Some Rules/Known Issue</h5> 
 
        <ul> 
 
         <li><small>Path need to be as a convex polygonal path with counterclockwise winding and no self intersections. The points are specified relative to the owning node’s origin. <a href="https://developer.apple.com/documentation/spritekit/skphysicsbody/1520379-bodywithpolygonfrompath" target="_blank">(documentation link)</a></small></li> 
 
         <li><small>Please use Chrome for best compatibility as I have not tested on other browsers.</small></li> 
 
        </ul> 
 
       </div> 
 
      </div> 
 

 

 
      <hr> 
 

 
      <div class="btn-group"> 
 
       <button class="btn btn-primary" type="button" onclick="resetShape()">Reset Shape</button> 
 
       <button class="btn btn-primary" type="button" onclick="location.reload()">Reset All</button> 
 
      </div> 
 
      <input type="checkbox" onclick="toggleRetinaMode()" id="retinaCheckbox" checked> Retina? (please check before declaring path) 
 
      <br><br> 
 

 
      <canvas id="sprite" width="940" height="100"></canvas> 
 
      <canvas id="path" width="0" height="100"></canvas> 
 

 
      <p class="text-muted"><small>X:<span id="tooltipX">0</span> Y:<span id="tooltipY">0</span></small></p> 
 
      <br> 
 

 
      <h5>Output</h5> 
 
<pre> 
 
SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithImageNamed:@"<span id="codeImgName">img</span>"]; 
 

 
CGFloat offsetX = sprite.frame.size.width * sprite.anchorPoint.x; 
 
CGFloat offsetY = sprite.frame.size.height * sprite.anchorPoint.y; 
 

 
CGMutablePathRef path = CGPathCreateMutable(); 
 

 
<span id="codeCGPath"></span> 
 
CGPathCloseSubpath(path); 
 

 
sprite.physicsBody = [SKPhysicsBody bodyWithPolygonFromPath:path]; 
 
</pre> 
 

 
     </div> 
 

 
     <script> 
 
// reference from http://davidwalsh.name/resize-image-canvas 
 

 
var spriteCanvas = document.getElementById('sprite'); 
 
var spriteContext = spriteCanvas.getContext('2d'); 
 
spriteContext.fillText('Drop Sprite Image Here', 400, 50); 
 

 
var pathCanvas = document.getElementById('path'); 
 
var pathContext = pathCanvas.getContext('2d'); 
 

 
function render(src){ 
 
    var image = new Image(); 
 
    image.onload = function(){ 
 
     spriteContext.clearRect(0, 0, spriteCanvas.width, spriteCanvas.height); 
 
     spriteCanvas.width = image.width; 
 
     spriteCanvas.height = image.height; 
 
     spriteContext.drawImage(image, 0, 0, image.width, image.height); 
 

 
     pathContext.clearRect(0, 0, pathCanvas.width, pathCanvas.height); 
 
     pathCanvas.width = image.width; 
 
     pathCanvas.height = image.height; 
 
    }; 
 
    image.src = src; 
 
} 
 

 
function loadImage(src){ 
 

 
    if(!src.type.match(/image.*/)){ 
 
     console.log('Dropped file is not image format'); 
 
     return; 
 
    } 
 

 
    var reader = new FileReader(); 
 
    reader.onload = function(e){ 
 
     render(e.target.result); 
 
    }; 
 
    reader.readAsDataURL(src); 
 

 
    var fileName = src.name; 
 
    var codeImgName = document.getElementById('codeImgName'); 
 
    codeImgName.innerHTML = fileName; 
 
} 
 

 
spriteCanvas.addEventListener('dragover', function(e){ 
 
    e.preventDefault(); 
 
}, true); 
 

 
spriteCanvas.addEventListener('drop', function(e){ 
 
    e.preventDefault(); 
 
    loadImage(e.dataTransfer.files[0]); 
 
}, true); 
 

 

 
var retinaMode = true; 
 
function toggleRetinaMode(){ 
 
    var status = document.getElementById('retinaCheckbox'); 
 

 
    retinaMode = status.checked ? true : false; 
 
} 
 

 

 

 
var actualX = 0; 
 
var actualY = 0; 
 
var displayX = document.getElementById('tooltipX'); 
 
var displayY = document.getElementById('tooltipY'); 
 

 
pathCanvas.onmousemove = function(e){ 
 
    actualX = e.pageX - this.offsetLeft; 
 
    actualY = e.pageY - this.offsetTop; 
 
    displayX.innerHTML = retinaMode ? Math.floor(actualX/2) : actualX; 
 
    displayY.innerHTML = retinaMode ? Math.floor((spriteCanvas.height - actualY - 1)/2) : spriteCanvas.height - actualY - 1; 
 
} 
 

 
var pathArray = new Array(); 
 
pathCanvas.onclick = function(e){ 
 
    var coor = { 
 
     actualX: actualX, 
 
     actualY: actualY, 
 
     displayX: displayX.innerHTML, 
 
     displayY: displayY.innerHTML, 
 
    }; 
 
    pathArray.push(coor); 
 
    refreshShape(pathArray); 
 
} 
 

 
var codeCGPath = document.getElementById('codeCGPath'); 
 
function refreshShape(pathArray){ 
 

 
    pathContext.clearRect(0, 0, pathCanvas.width, pathCanvas.height); 
 

 
    pathContext.beginPath(); 
 

 
    for(var i in pathArray){ 
 
     if(i == 0) { 
 
      pathContext.moveTo(pathArray[i].actualX, pathArray[i].actualY); 
 
      codeCGPath.innerHTML = 'CGPathMoveToPoint(path, NULL, '+pathArray[i].displayX+' - offsetX, '+pathArray[i].displayY+' - offsetY);<br>'; 
 
      continue; 
 
     } 
 
     pathContext.lineTo(pathArray[i].actualX, pathArray[i].actualY); 
 
     codeCGPath.innerHTML += 'CGPathAddLineToPoint(path, NULL, '+pathArray[i].displayX+' - offsetX, '+pathArray[i].displayY+' - offsetY);<br>'; 
 
    } 
 

 
    pathContext.closePath(); 
 
    pathContext.lineWidth = 1; 
 
    pathContext.strokeStyle = 'blue'; 
 
    pathContext.stroke(); 
 
    pathContext.fillStyle = 'blue'; 
 
    pathContext.fill(); 
 
} 
 

 
function resetShape(){ 
 
    pathArray = new Array(); 
 
    codeCGPath.innerHTML = null; 
 
    pathContext.clearRect(0, 0, pathCanvas.width, pathCanvas.height); 
 
} 
 
     </script> 
 
    </body> 
 
</html>

+1

Vâng! Điều này thật đúng với gì mà tôi đã tìm kiếm! Tôi nghĩ nhiều người đồng ý rằng những gì bạn đã làm là đáng để phát triển hơn nữa. Tôi có nơi nào tôi có thể viết yêu cầu tính năng không? – Darrarski

+1

@Darrarski, vâng. Chỉ cần để lại nó trong phần bình luận dưới đây. Tôi nhận thức được một số tính năng trong khi mã hóa nó nhưng tôi tạm thời để lại những thứ phức tạp để điền vào trong tương lai và có được cơ bản và chạy trong thời gian này (vì tôi đang trong quá trình phát triển trò chơi ngay bây giờ). Dù sao, tôi sẽ khuyến khích bạn để lại trong phần bình luận và những người khác có thể đọc trước khi đăng cùng một điều trên. :) – DazChong

+1

Đây là một trong những điều hữu ích nhất tôi đã thấy. Làm tốt lắm. – Grenter

1

tạo ảnh vui nhộn ít ứng dụng web, bởi DazChong . Và canot chờ đợi bản cập nhật của PhysicsEditor !!

này one cũng đang được phát triển

Bạn có thể tải nó trong giai đoạn alpha here

enter image description here

+0

Điều này thật tuyệt vời! Cám ơn vì đã chia sẻ. Có thể tên ứng dụng sẽ bao gồm 'SpriteKit' để tìm kiếm dễ dàng hơn. – DazChong

+0

@DazChong yeah có tiềm năng lớn.Tôi thích ứng dụng web của bạn, SpriteKit xấu hổ chỉ cho phép 12 điểm cho một con đường. Nếu bạn không biết, cũng có điều này nhưng cũng có thể xem http://www.paintcodeapp.com – DogCoffee

+0

vấn đề với 12 điểm cho con đường thậm chí còn tồi tệ hơn thế bởi vì nó phải về cơ bản là 12 điểm tham gia bởi các đường thẳng. Tôi đã cố gắng sử dụng hình dạng có 12 điểm nhưng đã sử dụng đường cong bezier giữa lúc đó và nhận được một thông điệp như "sử dụng hơn 300 điểm" hoặc bất kỳ số khổng lồ nào như thế. Apple đã làm một công việc lame với điều này và btw, SpriteKit là một túi lỗi: Tôi đã báo cáo ít nhất 20 lỗi tôi đã phát hiện trong một tuần sử dụng nó. – SpaceDog

8

Tôi tạo ra một trình soạn thảo và lớp loader để tạo SKPhysicsBodies phức tạp và nhập chúng vào mã của bạn. Nó cho phép bạn theo dõi xung quanh sprite của bạn, thêm nhiều cơ quan và xuất khẩu tất cả trong một giao diện khá đẹp. Hãy xem SKImport hereeditor.

Screencast

5

Tôi biết điều này hơi muộn, nhưng tôi vừa tạo một công cụ tuyệt vời cho mục đích này, tự động tạo một đường dẫn xung quanh ảnh sprite. bạn không phải tự bấm vào các điểm đó), và sau đó bạn có thể điều chỉnh các cài đặt khác nhau để phù hợp hơn với yêu cầu của bạn. Công cụ này cũng xuất ra cả mã chương trình Objective C và Swift để thêm đường dẫn tới một cơ thể vật lý sprite. Hy vọng nó hữu ích cho một số người.Thanks:

http://www.radicalphase.com/pathgen/

+0

quá xấu ... liên kết đã chết –

+0

xin lỗi vì tên miền đã hết hạn - công cụ đã hoạt động trở lại ngay bây giờ! :) –

+0

Bản cập nhật mới: Bây giờ bao gồm chế độ lồi lực (được bật theo mặc định) để đảm bảo khả năng tương thích 100% với Cơ quan Vật lý SpriteKit và cũng giảm số điểm - thưởng thức! :) –

4

Đây là kịch bản gốc (từ DazChong) thích nghi cho Swift

SKPhysicsBody Đường dẫn Generator Swift Version

<!DOCTYPE html> 
 
<html> 
 
<head> 
 
    <meta charset="UTF-8"> 
 
    <title>SpriteKit Tools - SKPhysicsBody Path Generator (Swift version </title> 
 
    <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css"> 
 

 
    <style> 
 
     /* disable responsive */ 
 
     .container { 
 
      max-width: none; 
 
      width: 970px; 
 
     } 
 

 
      #sprite { 
 
       background-color: #eee; 
 
       position: absolute; 
 
      } 
 
      #path { 
 
       cursor: crosshair; 
 
       opacity: 0.5; 
 
      } 
 
     </style> 
 

 
    </head> 
 
    <body> 
 
     <div class="container"> 
 
      <h1>SKPhysicsBody Path Generator</h1> 
 
      <p class="lead">Want to use SKPhysicsBody(polygonFromPath: path) easier way like me? Here with a small helper for easier path drawing, hope it help others too.</p> 
 
      <div class="row"> 
 
       <div class="col-md-6"> 
 
        <h5>Basic Instruction</h5> 
 
        <ol> 
 
         <li><small>Drag and drop the sprite image into drop zone.</small></li> 
 
         <li><small>Start drawing path by clicking on coordinates.</small></li> 
 
        </ol> 
 
       </div> 
 
       <div class="col-md-6"> 
 
        <h5>Some Rules/Known Issue</h5> 
 
        <ul> 
 
         <li><small>Path need to be as a convex polygonal path with counterclockwise winding and no self intersections. The points are specified relative to the owning node’s origin. <a href="https://developer.apple.com/library/ios/documentation/SpriteKit/Reference/SKPhysicsBody_Ref/Reference/Reference.html#//apple_ref/occ/clm/SKPhysicsBody/bodyWithPolygonFromPath:" target="_blank">(documentation link)</a></small></li> 
 
         <li><small>Please use Chrome for best compatibility as I have not tested on other browsers.</small></li> 
 
        </ul> 
 
       </div> 
 
      </div> 
 

 

 
      <hr> 
 

 
      <div class="btn-group"> 
 
       <button class="btn btn-primary" type="button" onclick="resetShape()">Reset Shape</button> 
 
       <button class="btn btn-primary" type="button" onclick="location.reload()">Reset All</button> 
 
      </div> 
 
      <input type="checkbox" onclick="toggleRetinaMode()" id="retinaCheckbox" checked> Retina? (please check before declaring path) 
 
      <br><br> 
 

 
      <canvas id="sprite" width="940" height="100"></canvas> 
 
      <canvas id="path" width="0" height="100"></canvas> 
 

 
      <p class="text-muted"><small>X:<span id="tooltipX">0</span> Y:<span id="tooltipY">0</span></small></p> 
 
      <br> 
 

 
      <h5>Output</h5> 
 
<pre> 
 
let sprite = SKSpriteNode(imageNamed: "codeImgName") 
 

 
let offsetX = sprite.size.width * sprite.anchorPoint.x 
 
let offsetY = sprite.size.height * sprite.anchorPoint.y 
 

 
let path = CGPathCreateMutable() 
 

 
<span id="codeCGPath"></span> 
 
CGPathCloseSubpath(path) 
 

 
sprite.physicsBody = SKPhysicsBody(polygonFromPath: path) 
 
</pre> 
 

 
     </div> 
 

 
     <script> 
 
// reference from http://davidwalsh.name/resize-image-canvas 
 

 
var spriteCanvas = document.getElementById('sprite'); 
 
var spriteContext = spriteCanvas.getContext('2d'); 
 
spriteContext.fillText('Drop Sprite Image Here', 400, 50); 
 

 
var pathCanvas = document.getElementById('path'); 
 
var pathContext = pathCanvas.getContext('2d'); 
 

 
function render(src){ 
 
    var image = new Image(); 
 
    image.onload = function(){ 
 
     spriteContext.clearRect(0, 0, spriteCanvas.width, spriteCanvas.height); 
 
     spriteCanvas.width = image.width; 
 
     spriteCanvas.height = image.height; 
 
     spriteContext.drawImage(image, 0, 0, image.width, image.height); 
 

 
     pathContext.clearRect(0, 0, pathCanvas.width, pathCanvas.height); 
 
     pathCanvas.width = image.width; 
 
     pathCanvas.height = image.height; 
 
    }; 
 
    image.src = src; 
 
} 
 

 
function loadImage(src){ 
 

 
    if(!src.type.match(/image.*/)){ 
 
     console.log('Dropped file is not image format'); 
 
     return; 
 
    } 
 

 
    var reader = new FileReader(); 
 
    reader.onload = function(e){ 
 
     render(e.target.result); 
 
    }; 
 
    reader.readAsDataURL(src); 
 

 
    var fileName = src.name; 
 
    var codeImgName = document.getElementById('codeImgName'); 
 
    codeImgName.innerHTML = fileName; 
 
} 
 

 
spriteCanvas.addEventListener('dragover', function(e){ 
 
    e.preventDefault(); 
 
}, true); 
 

 
spriteCanvas.addEventListener('drop', function(e){ 
 
    e.preventDefault(); 
 
    loadImage(e.dataTransfer.files[0]); 
 
}, true); 
 

 

 
var retinaMode = true; 
 
function toggleRetinaMode(){ 
 
    var status = document.getElementById('retinaCheckbox'); 
 

 
    retinaMode = status.checked ? true : false; 
 
} 
 

 

 

 
var actualX = 0; 
 
var actualY = 0; 
 
var displayX = document.getElementById('tooltipX'); 
 
var displayY = document.getElementById('tooltipY'); 
 

 
pathCanvas.onmousemove = function(e){ 
 
    actualX = e.pageX - this.offsetLeft; 
 
    actualY = e.pageY - this.offsetTop; 
 
    displayX.innerHTML = retinaMode ? Math.floor(actualX/2) : actualX; 
 
    displayY.innerHTML = retinaMode ? Math.floor((spriteCanvas.height - actualY - 1)/2) : spriteCanvas.height - actualY - 1; 
 
} 
 

 
var pathArray = new Array(); 
 
pathCanvas.onclick = function(e){ 
 
    var coor = { 
 
     actualX: actualX, 
 
     actualY: actualY, 
 
     displayX: displayX.innerHTML, 
 
     displayY: displayY.innerHTML, 
 
    }; 
 
    pathArray.push(coor); 
 
    refreshShape(pathArray); 
 
} 
 

 
var codeCGPath = document.getElementById('codeCGPath'); 
 
function refreshShape(pathArray){ 
 

 
    pathContext.clearRect(0, 0, pathCanvas.width, pathCanvas.height); 
 

 
    pathContext.beginPath(); 
 

 
    for(var i in pathArray){ 
 
     if(i == 0) { 
 
      pathContext.moveTo(pathArray[i].actualX, pathArray[i].actualY); 
 
      codeCGPath.innerHTML = 'CGPathMoveToPoint(path, nil, '+pathArray[i].displayX+' - offsetX, '+pathArray[i].displayY+' - offsetY)<br>'; 
 
      continue; 
 
     } 
 
     pathContext.lineTo(pathArray[i].actualX, pathArray[i].actualY); 
 
     codeCGPath.innerHTML += 'CGPathAddLineToPoint(path, nil, '+pathArray[i].displayX+' - offsetX, '+pathArray[i].displayY+' - offsetY)<br>'; 
 
    } 
 

 
    pathContext.closePath(); 
 
    pathContext.lineWidth = 1; 
 
    pathContext.strokeStyle = 'blue'; 
 
    pathContext.stroke(); 
 
    pathContext.fillStyle = 'blue'; 
 
    pathContext.fill(); 
 
} 
 

 
function resetShape(){ 
 
    pathArray = new Array(); 
 
    codeCGPath.innerHTML = null; 
 
    pathContext.clearRect(0, 0, pathCanvas.width, pathCanvas.height); 
 
} 
 
     </script> 
 
    </body> 
 
</html>

0

Bạn có thể chỉ làm điều này ngay bây giờ để gen tỷ lệ vật lý cơ thể từ PNG của bạn của sprite:

SKSpriteNode *yourPhysicsSprite = [SKSpriteNode spriteNodeWithImageNamed:@"yourPNG"]; 
yourPhysicsSprite.physicsBody = [SKPhysicsBody bodyWithTexture:yourPhysicsSprite.texture alphaThreshold:0.0f size:yourPhysicsSprite.texture.size]; 

Ít chính xác và có thể tốn kém hơn so với làm bằng tay, nhưng hoạt động tốt.

+0

Không thể thực hiện được trong SpriteKit khi tôi đặt câu hỏi. Bây giờ nó có thể, nhưng không hiệu quả lắm. – Darrarski

+0

Có thể thực hiện thế hệ đa giác này từ bên trong XCode ngay bây giờ không? Nếu vậy, nó ở đâu? Tôi đang gặp khó khăn với điều này. –

+0

Chỉ để mọi người biết, có một lỗi rất lớn trong iOS9 khiến bạn không thể sử dụng kết cấu để tạo ra các cơ quan vật lý chính xác. Điều kỳ lạ là nó hoạt động trong iOS8 và hoạt động trong iOS10 nhưng các vấn đề lớn trong iOS9, vẫn chưa được khắc phục kể từ hôm nay (mặc dù có một số cách giải quyết * không tuyệt vời). – Abhi

1

Đây là một sự thích nghi của câu trả lời Xelt trong Swift 3.

<!DOCTYPE html> 
 
<html> 
 
<head> 
 
    <meta charset="UTF-8"> 
 
    <title>SpriteKit Tools - SKPhysicsBody Path Generator (Swift version </title> 
 
    <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css"> 
 

 
    <style> 
 
     /* disable responsive */ 
 
     .container { 
 
      max-width: none; 
 
      width: 970px; 
 
     } 
 

 
      #sprite { 
 
       background-color: #eee; 
 
       position: absolute; 
 
      } 
 
      #path { 
 
       cursor: crosshair; 
 
       opacity: 0.5; 
 
      } 
 
     </style> 
 

 
    </head> 
 
    <body> 
 
     <div class="container"> 
 
      <h1>SKPhysicsBody Path Generator</h1> 
 
      <p class="lead">Want to use SKPhysicsBody(polygonFromPath: path) easier way like me? Here with a small helper for easier path drawing, hope it help others too.</p> 
 
      <div class="row"> 
 
       <div class="col-md-6"> 
 
        <h5>Basic Instruction</h5> 
 
        <ol> 
 
         <li><small>Drag and drop the sprite image into drop zone.</small></li> 
 
         <li><small>Start drawing path by clicking on coordinates.</small></li> 
 
        </ol> 
 
       </div> 
 
       <div class="col-md-6"> 
 
        <h5>Some Rules/Known Issue</h5> 
 
        <ul> 
 
         <li><small>Path need to be as a convex polygonal path with counterclockwise winding and no self intersections. The points are specified relative to the owning node’s origin. <a href="https://developer.apple.com/library/ios/documentation/SpriteKit/Reference/SKPhysicsBody_Ref/Reference/Reference.html#//apple_ref/occ/clm/SKPhysicsBody/bodyWithPolygonFromPath:" target="_blank">(documentation link)</a></small></li> 
 
         <li><small>Please use Chrome for best compatibility as I have not tested on other browsers.</small></li> 
 
        </ul> 
 
       </div> 
 
      </div> 
 

 

 
      <hr> 
 

 
      <div class="btn-group"> 
 
       <button class="btn btn-primary" type="button" onclick="resetShape()">Reset Shape</button> 
 
       <button class="btn btn-primary" type="button" onclick="location.reload()">Reset All</button> 
 
      </div> 
 
      <input type="checkbox" onclick="toggleRetinaMode()" id="retinaCheckbox" checked> Retina? (please check before declaring path) 
 
      <br><br> 
 

 
      <canvas id="sprite" width="940" height="100"></canvas> 
 
      <canvas id="path" width="0" height="100"></canvas> 
 

 
      <p class="text-muted"><small>X:<span id="tooltipX">0</span> Y:<span id="tooltipY">0</span></small></p> 
 
      <br> 
 

 
      <h5>Output</h5> 
 
<pre> 
 
let sprite = SKSpriteNode(imageNamed: "codeImgName") 
 

 
let offsetX = sprite.size.width * sprite.anchorPoint.x 
 
let offsetY = sprite.size.height * sprite.anchorPoint.y 
 

 
let path = CGMutablePath() 
 

 
<span id="codeCGPath"></span> 
 
path.closeSubpath() 
 

 
sprite.physicsBody = SKPhysicsBody(polygonFromPath: path) 
 
</pre> 
 

 
     </div> 
 

 
     <script> 
 
// reference from http://davidwalsh.name/resize-image-canvas 
 

 
var spriteCanvas = document.getElementById('sprite'); 
 
var spriteContext = spriteCanvas.getContext('2d'); 
 
spriteContext.fillText('Drop Sprite Image Here', 400, 50); 
 

 
var pathCanvas = document.getElementById('path'); 
 
var pathContext = pathCanvas.getContext('2d'); 
 

 
function render(src){ 
 
    var image = new Image(); 
 
    image.onload = function(){ 
 
     spriteContext.clearRect(0, 0, spriteCanvas.width, spriteCanvas.height); 
 
     spriteCanvas.width = image.width; 
 
     spriteCanvas.height = image.height; 
 
     spriteContext.drawImage(image, 0, 0, image.width, image.height); 
 

 
     pathContext.clearRect(0, 0, pathCanvas.width, pathCanvas.height); 
 
     pathCanvas.width = image.width; 
 
     pathCanvas.height = image.height; 
 
    }; 
 
    image.src = src; 
 
} 
 

 
function loadImage(src){ 
 

 
    if(!src.type.match(/image.*/)){ 
 
     console.log('Dropped file is not image format'); 
 
     return; 
 
    } 
 

 
    var reader = new FileReader(); 
 
    reader.onload = function(e){ 
 
     render(e.target.result); 
 
    }; 
 
    reader.readAsDataURL(src); 
 

 
    var fileName = src.name; 
 
    var codeImgName = document.getElementById('codeImgName'); 
 
    codeImgName.innerHTML = fileName; 
 
} 
 

 
spriteCanvas.addEventListener('dragover', function(e){ 
 
    e.preventDefault(); 
 
}, true); 
 

 
spriteCanvas.addEventListener('drop', function(e){ 
 
    e.preventDefault(); 
 
    loadImage(e.dataTransfer.files[0]); 
 
}, true); 
 

 

 
var retinaMode = true; 
 
function toggleRetinaMode(){ 
 
    var status = document.getElementById('retinaCheckbox'); 
 

 
    retinaMode = status.checked ? true : false; 
 
} 
 

 

 

 
var actualX = 0; 
 
var actualY = 0; 
 
var displayX = document.getElementById('tooltipX'); 
 
var displayY = document.getElementById('tooltipY'); 
 

 
pathCanvas.onmousemove = function(e){ 
 
    actualX = e.pageX - this.offsetLeft; 
 
    actualY = e.pageY - this.offsetTop; 
 
    displayX.innerHTML = retinaMode ? Math.floor(actualX/2) : actualX; 
 
    displayY.innerHTML = retinaMode ? Math.floor((spriteCanvas.height - actualY - 1)/2) : spriteCanvas.height - actualY - 1; 
 
} 
 

 
var pathArray = new Array(); 
 
pathCanvas.onclick = function(e){ 
 
    var coor = { 
 
     actualX: actualX, 
 
     actualY: actualY, 
 
     displayX: displayX.innerHTML, 
 
     displayY: displayY.innerHTML, 
 
    }; 
 
    pathArray.push(coor); 
 
    refreshShape(pathArray); 
 
} 
 

 
var codeCGPath = document.getElementById('codeCGPath'); 
 
function refreshShape(pathArray){ 
 

 
    pathContext.clearRect(0, 0, pathCanvas.width, pathCanvas.height); 
 

 
    pathContext.beginPath(); 
 

 
    for(var i in pathArray){ 
 
     if(i == 0) { 
 
      pathContext.moveTo(pathArray[i].actualX, pathArray[i].actualY); 
 
      codeCGPath.innerHTML = 'path.move(to: CGPoint(x: '+pathArray[i].displayX+' - offsetX, y: '+pathArray[i].displayY+' - offsetY))<br>'; 
 
      continue; 
 
     } 
 
     pathContext.lineTo(pathArray[i].actualX, pathArray[i].actualY); 
 
     codeCGPath.innerHTML += 'path.addLine(to: CGPoint(x: '+pathArray[i].displayX+' - offsetX, y: '+pathArray[i].displayY+' - offsetY))<br>'; 
 
    } 
 

 
    pathContext.closePath(); 
 
    pathContext.lineWidth = 1; 
 
    pathContext.strokeStyle = 'blue'; 
 
    pathContext.stroke(); 
 
    pathContext.fillStyle = 'blue'; 
 
    pathContext.fill(); 
 
} 
 

 
function resetShape(){ 
 
    pathArray = new Array(); 
 
    codeCGPath.innerHTML = null; 
 
    pathContext.clearRect(0, 0, pathCanvas.width, pathCanvas.height); 
 
} 
 
     </script> 
 
    </body> 
 
</html>