จะใช้เอฟเฟกต์ทางกายภาพใน SimCity 5 ได้อย่างไรว่าอาคารจะแกว่งเมื่อถูกย้าย?

เป็นการยากที่จะอธิบายเป็นข้อความธรรมดา ดังนั้นฉันจึงบันทึก GIF ไว้เพื่อสาธิต

https://public.lightpic.info/image/2B1F_582465841.gif

ขณะนี้ฉันกำลังทำโครงการที่ต้องการผลดังกล่าว จนถึงตอนนี้ ฉันได้สร้างเอฟเฟกต์ที่คล้ายกันบน iOS ด้วย SpriteKit เสร็จแล้ว แต่น่าเสียดายที่ผลลัพธ์ออกมาไม่น่าพอใจนัก

นี่คืองานของฉัน: ในโครงการของฉัน มีวัตถุที่มีรูปร่างไม่ปกติ มีการใช้แรงคงที่ที่จุดใดจุดหนึ่งใต้จุดศูนย์กลางแรงโน้มถ่วงของวัตถุและมีทิศทางลดลง ฉันตั้งกฎไว้ว่าพิกัดของวัตถุไม่สามารถเปลี่ยนแปลงได้ด้วยแรง ดังนั้นวัตถุจะไม่ถูกแรงดึงดึงลง เมื่อวัตถุเอียง ความเฉื่อยและแรงจะส่งผลให้เกิดโมเมนตัมเชิงมุม ซึ่งทำให้วัตถุตั้งตรง รายละเอียด:

บังคับสาธิต

และเมื่อวัตถุถูกจัดการ จุดกระทำจะสูงกว่าจุดศูนย์ถ่วง โมเมนตัมเชิงมุมทำให้วัตถุเอียง โดยเป็นผลจากการรวมกันของความเฉื่อยและแรงของวัตถุ

อย่างไรก็ตาม ไม่มีอะไรพิสูจน์ได้ว่าฉันพูดถูก เพราะผลลัพธ์ที่ฉันได้รับจากเครื่องมือฟิสิกส์ของ SpriteKit ก็คือ วัตถุนั้นกลายเป็นลูกตุ้มธรรมดา แต่สัญชาตญาณบอกฉันว่าฉันไม่ผิด จริงๆ แล้วฉันไม่ได้ตรึงจุดศูนย์ถ่วงของวัตถุไว้ที่พื้นหลัง และสิ่งที่ฉันทำจริงๆ คือนำวัตถุกลับไปที่ตำแหน่งเดิมทุกครั้งที่จำลองฟิสิกส์ แต่ผลลัพธ์ก็ตบหน้าฉันแรงมาก :( ดังนั้นลูกตุ้มจึงแกว่งไปมา ผลกระทบทางกายภาพขั้นสุดท้ายก็แค่ห่วย

จากนั้นฉันก็หาวิธีแก้ปัญหาชั่วคราว: คูณความเร็วเชิงมุมด้วย 0.95 ทุกครั้งที่จำลองฟิสิกส์ เห็นได้ชัดว่าวิธีแก้ปัญหานี้ไม่ใช่วิธีแก้ปัญหาในอุดมคติ เพราะเมื่อมุมการหมุนมีแนวโน้มเป็นแนวนอน ความเร็วเชิงมุมจะไม่สูงเพียงพอและจะตั้งตรงอย่างช้าๆ อย่างไรก็ตาม ยังมีความก้าวหน้าอยู่: อย่างน้อยที่สุด วัตถุก็สามารถหยุดการแกว่งได้ในที่สุด

วิธีแก้ไขเพิ่มเติมของฉันคือแรงที่ใช้กับวัตถุจะเปลี่ยนไปตามระดับความเอียง เมื่อวัตถุมีแนวโน้มอยู่ในแนวนอน แรงจะมีขนาดใหญ่ และเมื่อวัตถุมีแนวโน้มอยู่ในแนวตั้ง แรงจะมีขนาดเล็ก ฟังก์ชันง่ายๆ อธิบายได้ดี: F=1000N×|sin[rotation องศา]| สิ่งนี้ช่วยได้มาก แต่น่าเสียดายที่ผลลัพธ์ที่ได้ดูเหมือนจะไม่เกิดขึ้นจริงเลย

สรุป: หลังจากศึกษามาหลายวัน ฉันล้มเหลวในการใช้เอฟเฟกต์ที่แสดงใน GIF และฉันรู้สึกละอายใจมากกับสิ่งนั้น ฉันหวังเป็นอย่างยิ่งว่าใครก็ตามที่มีความสามารถมากจะช่วยฉันได้ ขอบคุณที่อ่านคำอธิบายยาวๆ ของฉัน ฉันซาบซึ้งจริงๆ ที่อดทนรอ

ส่วนที่เพิ่มเข้าไป:

มีภาพหน้าจอที่แสดงแนวทางของฉันในการใช้เอฟเฟกต์นี้

การใช้งานของฉัน

นอกจากนี้ 2:

ฉันได้อัปโหลดการใช้งานซึ่งเป็นไฟล์ Swift Playground แล้ว ดาวน์โหลดได้ที่: http://www.mediafire.com/file/qrct5sty2cyvwsy/Swing.playground.zip

ป.ล. เนื่องจากภาษาแม่ของฉันไม่ใช่ภาษาอังกฤษ โปรดยกโทษให้ไวยากรณ์ที่ไม่ดีของฉันด้วย


person Source    schedule 11.11.2016    source แหล่งที่มา
comment
แกนที่มันแกว่งอยู่คือ x หรือประมาณนั้น สิ่งนี้จำเป็นต้องใช้ฉาก 3 มิติพร้อมการแปลง 3 มิติ คุณช่วยจับภาพหน้าจอหรือแสดงให้ฉันเห็นว่าคุณจัดฉากในแบบ 2D อย่างไร เพื่อที่ฉันจะได้อธิบายวิธีแก้ปัญหานี้ได้   -  person Confused    schedule 12.11.2016
comment
@ สับสนใช่ แกนคือ x ฉันได้เพิ่มภาพหน้าจอและอาจมีประโยชน์ ขอบคุณมากสำหรับการตอบคำถามของฉัน   -  person Source    schedule 12.11.2016
comment
เฮ้ Source ลิงก์นั้นเสียหรือแปลกหรือเป็นของท้องถิ่นหรืออะไรสักอย่าง ไม่มีอะไรที่นั่น คุณสามารถแทรกรูปภาพในคำถามของคุณได้ เพียงกดปุ่มรูปภาพขณะแก้ไข ในแถวสไตล์/การแก้ไขที่อยู่ด้านบนกล่องข้อความ   -  person Confused    schedule 12.11.2016
comment
@สับสน ขออภัยที่ทำให้คุณไม่สะดวก ฉันได้แก้ไขสิ่งนั้นแล้ว และคุณเห็นรูปภาพตอนนี้หรือไม่?   -  person Source    schedule 12.11.2016
comment
ขออภัย เคยเห็นอันนี้แล้ว สิ่งที่ฉันต้องการเห็น (เพื่อลองช่วย) คือสิ่งที่คุณกำลังทำอยู่ และวิธีที่คุณใช้พื้นที่ 2 มิติและ 3 มิติ ดังนั้นฉันจึงสามารถให้คำแนะนำได้ไม่เพียงแต่เกี่ยวกับวิธีการรับความรู้สึกที่คุณกำลังมองหาเท่านั้น แต่ยัง วิธีทำให้มันใช้งานได้ในพื้นที่ของคุณ แก้ไข: อ๊ะ เบราว์เซอร์ที่รีเฟรชและสามารถดูภาพของคุณได้แล้ว   -  person Confused    schedule 12.11.2016
comment
สิ่งที่ฉันเห็นคือการมองลงไปตรงๆ บนบางสิ่งที่กำลังเคลื่อนที่ในพื้นที่ X/Y ฉันเดาว่าคุณต้องการให้สิ่งนี้หมุนบนแกน X/Y ที่อยู่ในมุมฉากกับเส้นทางการเคลื่อนที่ แต่นั่นจะยุ่งยากสักหน่อย หากไม่ใช่ว่าเป็นไปไม่ได้เลยใน SpriteKit คุณสามารถใช้ SceneKit เพื่อโฮสต์วัตถุนี้ในพื้นที่ 3 มิติได้หรือไม่?   -  person Confused    schedule 12.11.2016
comment
@สับสน คุณเดาถูกแล้ว เอฟเฟกต์ที่แท้จริงที่โปรเจ็กต์ของฉันต้องการก็คือ เมื่อวัตถุถูกย้ายในแกน X และ Y วัตถุจะหมุนเป็นมุมฉาก ฉันได้อัปโหลด Swift Playground ซึ่งแสดงให้เห็นถึงทฤษฎีของฉัน ฉันไม่สามารถเพิ่มเอฟเฟกต์นี้ให้กับโปรเจ็กต์ของฉันได้ เนื่องจากใน SKScene touchesMoved() จะส่งกลับการเคลื่อนไหวเล็กน้อยทุกครั้งที่อัปเดต ซึ่งทำให้พฤติกรรมของวัตถุของฉันแปลกมาก และการใช้ SceneKit อาจเป็นประโยชน์ แต่จริงๆ แล้วฉันยังใหม่กับ SpriteKit และแทบไม่มีประสบการณ์ใน SceneKit ดังนั้น ฉันจึงอาจใช้เอฟเฟกต์นี้ไม่ได้ในที่สุด อย่างไรก็ตาม ขอบคุณสำหรับความช่วยเหลือและความอดทนของคุณ   -  person Source    schedule 13.11.2016


คำตอบ (1)


สปริง สวิงอาร์ม และสนิมบ้าง..

สิ่งที่คุณต้องการคือสวิงอาร์มที่ติดอยู่กับสปริงและแดมเปอร์ที่เคลื่อนที่ได้บนรถเข็น

สปริงใช้แรงเพื่อคืนสวิงอาร์มกลับสู่ตำแหน่งตั้งตรง

การหน่วง (ในการสาธิตจะใช้กับข้อต่อการหมุนเนื่องจากแรงเสียดทาน เช่น ที่พบในข้อต่อที่เป็นสนิม) อยู่ที่นั่นเพียงเพื่อหยุดการสั่นตลอดไป

การเลื่อนเมาส์จะออกแรงไปในทิศทางตรงกันข้ามที่ด้านบนของสวิงอาร์ม

สวิงอาร์มลูกตุ้ม V

ความแตกต่างประการหนึ่งระหว่างลูกตุ้มและสวิงอาร์มรับแรงสปริงก็คือ ความถี่ของการแกว่งจะเปลี่ยนไปตามปริมาณโมเมนตัมเชิงมุม แรงตึงขั้นต่ำของสปริง และตำแหน่งของสวิงอาร์ม

ภาพประกอบแบบโต้ตอบ

ภาพประกอบแสดงให้เห็นการทำงานของสวิงอาร์ม แต่ลักษณะของสวิงอาร์มนั้นขึ้นอยู่กับหลายปัจจัย ความสูงของสวิงอาร์ม ความแข็งแรงของสปริง การหน่วง มวลของสวิงอาร์ม โดยที่สปริงจะติดตั้งอยู่บนแขนและยึดเข้ากับรถเข็นที่กำลังเคลื่อนที่ ฉันได้เพิ่มแถบเลื่อนเพื่อให้คุณเห็นพฤติกรรมต่างๆ

ภาพประกอบไม่ใช่คำตอบ แต่มีไว้เพื่อแสดงแนวคิดเท่านั้น คุณจะต้องนำโซลูชันไปใช้งานในซอฟต์แวร์หรือไลบรารีใดก็ตามที่คุณใช้

สปริงมีลักษณะเรียบง่ายมาก โดยมีความสัมพันธ์เชิงเส้นตรงระหว่างความยาวและแรงของสปริง ค้นหากฎของฮุก

การหน่วงในตัวอย่างเป็นเพียงสเกลาร์ที่ใช้กับการหมุนแบบเดลต้า dr *= 1-damping

แรงที่ส่งไปยังสวิงอาร์ม ณ ตำแหน่งหนึ่งเป็นการเร่งความเร็วและแรง (นิวตันในหน่วยพิกเซล BS) จุดหมุนได้รับการแก้ไขแล้ว ดังนั้นความเร่งเชิงเส้นจึงสูญเสียไป

อัปเดต. มีข้อผิดพลาดเล็กน้อยในโพสต์แรก ฉันใช้แรงสปริงเป็นการเร่งความเร็วมากกว่าแรง ไม่สามารถดึงสปริงให้ตึงได้ และการเคลื่อนไหวของเมาส์ถูกแปลงเป็นการเร่งความเร็วอย่างไม่ถูกต้อง ความแตกต่างนั้นลึกซึ้งแต่สำคัญ แก้ไขทั้งหมดแล้ว ???? สนุกได้เลย ????

var canvas = document.createElement("canvas");
canvas.width = innerWidth - 40;
canvas.height = innerHeight - 40;
canvas.style.border = "1px solid black";
var ctx = canvas.getContext("2d");
document.body.appendChild(canvas);
var sliderChanged = true;
function createSlider(name,val,min,max){
var div = document.createElement("div");    
div.textContent = name;
var slider = document.createElement("input");
var valSpan = document.createElement("span");    
slider.type = "range";
slider.min = min;
slider.max = max;

slider.step = (max-min)/Math.floor(canvas.width * 0.7);
slider.value = val;
valSpan.textContent = val;
slider.addEventListener("mousemove",function(){
    if(slider.value !== slider.lastValue){
        slider.lastValue = slider.value;
        valSpan.textContent = Number(slider.value).toFixed(3);
        sliderChanged = true;
     }
});
div.appendChild(slider);
div.appendChild(valSpan);
document.body.appendChild(div);
return slider;
}

var springTension = createSlider("Spring tension :",0.5,0,1);
var springStrength = createSlider("Spring strength :",5,0.1,20);
var damping = createSlider("Damping :",0.1,0.01,1.0);
var armMass = createSlider("Swing arm mass:",200,1,1000);
var armHeight = createSlider("Swing arm height:",Math.floor(canvas.height * 0.6),Math.floor(canvas.height * 0.1),Math.floor(canvas.height * 0.8));



var mouse = (function () {
function preventDefault(e) {
    e.preventDefault();
}
var mouse = {
    x : 0,
    y : 0,
    bounds : null,
    mouseEvents : "mousemove".split(",")
};
var m = mouse;
function mouseMove(e) {
    var t = e.type;
    m.bounds = m.element.getBoundingClientRect();
    m.x = e.pageX - m.bounds.left;
    m.y = e.pageY - m.bounds.top;
}
m.updateBounds = function () {
}
m.start = function (element) {
    m.element = element === undefined ? document : element;
    m.mouseEvents.forEach(n => {
        m.element.addEventListener(n, mouseMove);
    });
    m.updateBounds();
}
return mouse;
})();
mouse.start(canvas);

//=====================================================================================================================
// Answer start here

const springCof = 0.3;  // characteristic of the spring see Hooks law
const dampingC = 0.05;  // amount of damping as a factor of rotational speed.
const springTensionC = 0.5; // min tension on the spring ( 1 subtract the amount the spring is stretched from relaxed length)

// details of the swing arm

var pole = {};
pole.mass = 200;
pole.dr = 0;
pole.rot = 0;
pole.piviotRadius = canvas.height * 0.01;
pole.topWidth = canvas.height * 0.02
pole.centerWidth = canvas.height * 0.04
pole.baseWidth = canvas.height * 0.02
pole.x = canvas.width / 2;
pole.y = canvas.height * 0.7;
pole.height = canvas.height * 0.6; // from rotation point to top
pole.baseHeight = canvas.height * 0.1;
pole.spring = {};
pole.spring.y = canvas.height * 0.1;
pole.spring.x = 0;
pole.spring.baseY = canvas.height * 0.2;
pole.spring.baseX = 0;
pole.spring.relaxLength = Math.hypot(pole.spring.x -pole.spring.baseX, pole.spring.y - pole.spring.baseY);
pole.spring.relaxLength *= springTensionC;
pole.spring.cof = springCof;  // characteristic of the spring see Hooks law
pole.spring.damp = dampingC;  // amount of damping as a factor of rotational speed.
                      // Basicly the pivot is rusty and provides the damping.
function setPoleValues(pole){
pole.height = Number(armHeight.value);
pole.mass = Number(armMass.value);
var lookRight = Math.pow(pole.mass,1/3)/Math.pow(1000,1/3);
pole.topWidth = canvas.height * (0.001 + 0.02 * lookRight);
pole.centerWidth = canvas.height * (0.004 + 0.04 * lookRight)
pole.baseWidth = canvas.height * (0.004 + 0.02 * lookRight)
pole.spring.relaxLength = Math.hypot(pole.spring.x -pole.spring.baseX, pole.spring.y - pole.spring.baseY);
pole.spring.relaxLength *= 1-Number(springTension.value);
pole.spring.cof = Number(springStrength.value);  
pole.spring.damp = Number(damping.value);
}

// draws a spring
function drawSpring(x1,y1,x2,y2,width){
var x = x2 - x1;
var y = y2 - y1;
var dist = Math.sqrt(x * x + y * y);

var nx = x / dist;
var ny = y / dist;
ctx.beginPath();
ctx.lineWidth = 1;
ctx.moveTo(x1,y1);
var step = 0.1;
for(var i = step; i < 1-step; i += step){
    for(var j = 0; j < 1; j += 0.1){
        var xx = x1 + x * (i + j * step);
        var yy = y1 + y * (i + j * step);
        xx -= Math.sin(j * Math.PI * 2) * ny * width;
        yy += Math.sin(j * Math.PI * 2) * nx * width;
        ctx.lineTo(xx,yy);
    }
}
ctx.lineTo(x2,y2);
ctx.stroke();
return dist;
}


// draws the pole and also calculates the position of the pole top
// and details about the spring
function drawPole(pole){
ctx.fillStyle = "red";
ctx.strokeStyle = "black";
ctx.lineWidth = 4;
ctx.lineJoin = "round";
ctx.setTransform(1,0,0,1,pole.x,pole.y)
ctx.rotate(pole.rot)
ctx.beginPath();
ctx.moveTo( - pole.topWidth,- pole.height);
ctx.lineTo(pole.topWidth,- pole.height);
ctx.lineTo(pole.centerWidth,0);
ctx.lineTo(pole.baseWidth, pole.baseHeight);
ctx.lineTo( - pole.baseWidth, pole.baseHeight);
ctx.lineTo( - pole.centerWidth,0);
ctx.closePath();
ctx.stroke();
ctx.fill();
ctx.fillStyle = "yellow";
ctx.beginPath();
ctx.arc(  pole.spring.x,pole.spring.y,pole.piviotRadius * 0.5,0,Math.PI*2);
ctx.stroke();
ctx.fill();
ctx.setTransform(1,0,0,1,0,0)
ctx.fillStyle = "blue";
ctx.beginPath();
ctx.arc(pole.x,pole.y,pole.piviotRadius,0,Math.PI*2);
ctx.stroke();
ctx.fill();
ctx.fillStyle = "yellow";
ctx.beginPath();
ctx.arc(pole.x + pole.spring.baseX,pole.y  + pole.spring.baseY,pole.piviotRadius * 0.5,0,Math.PI*2);
ctx.stroke();
ctx.fill();


var xdx = Math.cos(pole.rot);
var xdy = Math.sin(pole.rot);
var xx = pole.spring.realX = xdx * pole.spring.x - xdy * pole.spring.y;
var yy = pole.spring.realY = xdy * pole.spring.x + xdx * pole.spring.y;
pole.spring.length = Math.hypot(pole.x + xx -(pole.x + pole.spring.baseX), pole.y + yy- (pole.y + pole.spring.baseY));
pole.spring.direction = Math.atan2(pole.y + pole.spring.baseY - (pole.y + yy),pole.x + pole.spring.baseX-(pole.x + xx ))
pole.topX = pole.x + xdy * pole.height; // at 90 deg
pole.topY = pole.y - xdx * pole.height;    
   drawSpring(pole.x + xx,pole.y  + yy,pole.x + pole.spring.baseX,pole.y  + pole.spring.baseY,3);

}
// applies a force.
// As the the swing arm rotation point is fixed this only extracts the 
// angular acceleration from the force
function applyAccel(pole,x,y,ax, ay){ // x,y where the force is applied,
                                  // ax,ay the acceleration of the force
var direction = Math.atan2(ay,ax);
var toCenter = Math.atan2(pole.y - y, pole.x - x);
var pheta = toCenter - direction;
var dist = Math.hypot(x-pole.x,y-pole.y);
var force = Math.hypot(ax,ay) * pole.mass;
var Fa = Math.sin(pheta) * force; 
Fa = Fa / (pole.mass * dist);
pole.dr += Fa;// now add that to the box delta r    
}
function applyForce(pole, x, y, fx, fy){ // x,y where the force is applied, 
                                     // fx,fy the force
var direction = Math.atan2(fy,fx);
var toCenter = Math.atan2(pole.y - y, pole.x - x);
var pheta = toCenter - direction;
var dist = Math.hypot(x-pole.x,y-pole.y);
var force = Math.hypot(fx,fy) ;
var Fa = Math.sin(pheta) * force; 
Fa = Fa / (pole.mass * dist);
pole.dr += Fa;// now add that to the box delta r    
}





// for calculating the acceleration of the mouse
var lastX = 0;
var speed = {};
speed.x = 0;
speed.y = 0;
speed.lx = 0;
speed.ly = 0;




function update2(timer){
globalTime = timer;
ctx.setTransform(1,0,0,1,0,0); // reset transform
ctx.globalAlpha = 1;           // reset alpha
ctx.clearRect(0,0,canvas.width,canvas.height);
if(sliderChanged){
    setPoleValues(pole);
    sliderChanged;
}

if(lastX == undefined){
    lastX = mouse.x;
    getPoleDetails(pole);
}
    drawPole(pole);
// move the pole
pole.x = mouse.x;
// get the acceleration of the mouse movement
speed.x = (lastX - mouse.x);
speed.y = 0;

// apply the mouse movement acceleration to the top of the pole
// Accel is the change in mouse speed
applyAccel(pole,pole.topX,pole.topY,speed.x - speed.lx, speed.y - speed.ly);

// apply the springs force (note the spring is never compressed)
applyForce(
    pole,
    pole.x + pole.spring.realX, 
    pole.y + pole.spring.realY,
    Math.cos(pole.spring.direction) * (pole.spring.length - pole.spring.relaxLength) * pole.spring.cof,
    Math.sin(pole.spring.direction) * (pole.spring.length - pole.spring.relaxLength) * pole.spring.cof
)
// add the change in rotation
pole.rot += pole.dr;
// dampen the rotation 
pole.dr *= 1-pole.spring.damp;

lastX = mouse.x
speed.lx = speed.x;
speed.ly = speed.y
if((mouse.buttonRaw & 4)!== 4){
    requestAnimationFrame(update2);
}else{
    log("done");
}
}
requestAnimationFrame(update2);

person Blindman67    schedule 15.11.2016
comment
ขอบคุณมาก! สปริงจำลองได้ดีมาก! - person Source; 19.11.2016