Bagaimana cara menerapkan efek fisik di SimCity 5, yaitu bangunan berayun saat dipindahkan?

Sulit untuk dijelaskan dalam teks biasa, jadi saya merekam GIF untuk mendemonstrasikannya.

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

Saat ini saya sedang mengerjakan proyek yang memerlukan efek seperti itu. Sampai saat ini saya sudah menyelesaikan efek serupa di iOS dengan SpriteKit, namun sayangnya hasilnya kurang memuaskan.

Ini pekerjaan saya: dalam proyek saya ada sebuah benda yang bentuknya tidak beraturan, gaya konstan diterapkan di suatu tempat di bawah pusat gravitasi benda dan arahnya ke bawah. Saya telah membuat aturan bahwa koordinat benda tidak dapat diubah oleh gaya, sehingga benda tidak akan tertarik ke bawah oleh gaya. Ketika benda dimiringkan, kelembaman dan gayanya menghasilkan momentum sudut, yang menjadikan benda tegak. Detail:

Paksa Demo

Dan ketika objek sedang dimanipulasi, titik aksinya lebih tinggi dari pusat gravitasi. Akibat kombinasi inertansi dan gaya benda, momentum sudut membuat benda menjadi miring.

Namun belum ada yang membuktikan kebenaran saya, karena hasil yang saya peroleh dari mesin fisika SpriteKit adalah, benda tersebut ternyata berupa pendulum sederhana. Namun intuisi memberitahu saya bahwa saya tidak salah, sebenarnya saya tidak menyematkan pusat gravitasi objek ke latar belakang, dan yang sebenarnya saya lakukan adalah mengembalikan objek ke posisi semula setiap kali fisika disimulasikan. Tapi hasilnya menampar mukaku dengan keras :(. Makanya pendulumnya berayun-ayun, efek fisik akhirnya jelek banget.

Kemudian saya menemukan solusi sementara: mengalikan kecepatan sudut dengan 0,95 setiap kali fisika disimulasikan. Solusi ini jelas bukan solusi ideal, karena ketika sudut rotasi cenderung horizontal, kecepatan sudutnya tidak cukup tinggi dan perlahan menjadi tegak. Namun ada kemajuan: setidaknya benda tersebut akhirnya bisa berhenti berayun.

Solusi saya selanjutnya adalah gaya yang diterapkan pada benda berubah seiring dengan tingkat kemiringannya. Jika benda cenderung horizontal maka gayanya cenderung besar, dan jika benda cenderung vertikal maka gayanya cenderung kecil. Fungsi sederhana akan menjelaskannya dengan baik: F=1000NĂ—|sin[derajat rotasi]|. Ini sangat membantu, namun sayangnya efek yang dihasilkan tidak terlihat secara fisik sama sekali.

Kesimpulan: setelah belajar selama beberapa hari, saya gagal menerapkan efek yang ditunjukkan dalam GIF, dan saya merasa sangat malu karenanya. Saya sangat berharap siapa pun yang memiliki kemampuan hebat akan membantu saya. Terima kasih telah membaca uraian panjang saya, saya sangat menghargai kesabaran Anda.

Tambahan:

Ada tangkapan layar yang menunjukkan pendekatan saya untuk menerapkan efek ini.

Implementasi saya

Tambahan 2:

Saya telah mengunggah implementasi saya, yaitu file Swift Playground. Unduh di: http://www.mediafire.com/file/qrct5sty2cyvwsy/Swing.playground.zip

PS. Karena bahasa ibu saya bukan bahasa Inggris, mohon maafkan tata bahasa saya yang buruk.


person Source    schedule 11.11.2016    source sumber
comment
Sumbu yang diayunkannya adalah x, atau sekitar itu. Hal ini memerlukan penggunaan adegan 3D dengan transformasi 3D-nya. Bisakah Anda mengambil tangkapan layar atau menunjukkan kepada saya bagaimana Anda mengatur adegan Anda dalam 2D, sehingga saya dapat menjelaskan cara mengatasi masalah ini.   -  person Confused    schedule 12.11.2016
comment
@Bingung ya, sumbunya x. Saya telah menambahkan tangkapan layar dan semoga bermanfaat. Terima kasih banyak telah menjawab pertanyaan saya.   -  person Source    schedule 12.11.2016
comment
Hai Sumber, tautan itu mati, aneh, lokal, atau semacamnya. Tidak ada apa pun di sana. Anda dapat memasukkan gambar ke dalam pertanyaan Anda, cukup tekan tombol gambar saat mengedit, di baris gaya/pengeditan tepat di atas kotak teks.   -  person Confused    schedule 12.11.2016
comment
@Bingung, maaf telah membuat Anda tidak nyaman. Saya telah memperbaikinya, dan dapatkah Anda melihat gambarnya sekarang?   -  person Source    schedule 12.11.2016
comment
Maaf, saya pernah melihat yang ini. Apa yang ingin saya lihat (untuk mencoba bantuan) adalah apa yang Anda lakukan, dan bagaimana Anda menggunakan ruang, 2D dan 3D, sehingga saya dapat memberikan saran tidak hanya tentang cara mendapatkan nuansa yang Anda cari, tetapi juga bagaimana membuatnya berfungsi di ruangan Anda. EDIT: ups. browser yang disegarkan, dan dapat melihat gambar Anda sekarang.   -  person Confused    schedule 12.11.2016
comment
Apa yang saya lihat adalah pandangan lurus ke bawah pada sesuatu yang bergerak di ruang X/Y. Saya kira Anda ingin ini berputar pada sumbu X/Y yang tegak lurus dengan jalur perjalanannya. Tapi itu akan menjadi sedikit rumit, atau bahkan mustahil di SpriteKit. Bisakah Anda menggunakan SceneKit untuk menghosting objek ini dalam ruang 3D?   -  person Confused    schedule 12.11.2016
comment
@Bingung Anda menebaknya dengan benar. Efek nyata yang dibutuhkan proyek saya adalah, ketika objek dipindahkan pada sumbu X dan Y, objek tersebut berputar ke sudut kanan. Saya telah mengunggah Swift Playground, yang mendemonstrasikan teori saya. Saya gagal menambahkan efek ini ke proyek saya, karena dalam SKScene, touchesMoved() mengembalikan gerakan yang sangat kecil setiap pembaruan, yang membuat perilaku objek saya menjadi sangat aneh. Dan mungkin berguna untuk menggunakan SceneKit, tetapi sebenarnya saya baru mengenal SpriteKit, dan hampir tidak memiliki pengalaman dalam SceneKit, jadi saya mungkin tidak dapat menerapkan efek ini pada akhirnya. Bagaimanapun, terima kasih atas bantuan dan kesabaran Anda.   -  person Source    schedule 13.11.2016


Jawaban (1)


Pegas, lengan ayun, dan sedikit karat..

Yang Anda incar adalah lengan ayun yang dipasang pada pegas dan peredam yang digerakkan pada troli.

Pegas memberikan gaya untuk mengembalikan lengan ayun ke posisi tegak.

Peredam (dalam demo diterapkan pada sambungan rotasi sebagai gesekan seperti yang ditemukan pada sambungan berkarat) hanya untuk menghentikannya berosilasi selamanya.

Menggerakan mouse akan menerapkan gaya berlawanan arah di bagian atas lengan ayun.

Lengan Ayun Pendulum V

Salah satu perbedaan antara bandul dan lengan ayun yang dibebani pegas adalah frekuensi osilasi akan berubah tergantung pada besarnya momentum sudut, tegangan minimum pegas, dan posisi lengan ayun.

Ilistrasi interaktif

Ilustrasi tersebut menunjukkan lengan ayun sedang beraksi, tetapi karakteristik lengan ayun bergantung pada banyak faktor; Ketinggian lengan ayun, kekuatan pegas, redaman, massa lengan ayun, tempat pegas dipasang pada lengan dan dipasang pada troli yang bergerak. Saya telah menambahkan beberapa bilah geser agar Anda dapat melihat perilaku yang berbeda.

Ilistrasi bukanlah sebuah jawaban, ini hanya untuk mengilustrasikan konsep, Anda harus mengimplementasikan solusi tersebut dalam perangkat lunak atau perpustakaan apa pun yang Anda gunakan.

Pegas sangat sederhana, dengan hubungan linier antara panjang dan gaya pegas. Hukum Kait Pencarian.

Redaman pada contoh hanyalah skalar yang diterapkan pada rotasi delta. dr *= 1-damping

Gaya diterapkan pada lengan ayun di suatu lokasi sebagai percepatan dan gaya (newton dalam satuan piksel BS). Porosnya tetap dan dengan demikian percepatan linier apa pun hilang.

Memperbarui. Ada sedikit kesalahan pada postingan pertama. Saya menerapkan gaya pegas sebagai percepatan, bukan gaya, pegas tidak dapat ditarik, dan gerakan mouse salah diubah menjadi percepatan. Perbedaannya tidak kentara namun penting. Semua sudah diperbaiki ???? selamat menikmati ????

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
Terima kasih banyak! Pegas mensimulasikannya dengan sangat baik! - person Source; 19.11.2016