Roblox
Belajar Luau 12: Efek, Suara, Animasi Tween, dan Badge
Membuat game Roblox terasa menyenangkan: animasi halus dengan TweenService, memilih efek di server atau client, suara, partikel, dan memberi badge pencapaian.

Game yang fungsinya sudah benar belum tentu terasa menyenangkan. Pelajaran ini membahas "polish": gerakan halus, suara, partikel, dan badge (pencapaian). Hal-hal kecil seperti ini sangat berpengaruh pada apakah pemain mau bertahan.
| File | Jenis | Diletakkan di |
|---|---|---|
1-tween.server.luau |
Script | ServerScriptService |
2-efek-koin.client.luau |
LocalScript | StarterPlayer → StarterPlayerScripts |
3-efek-checkpoint-badge.server.luau |
Script | ServerScriptService |
Butuh: leaderstats dan checkpoint (Pelajaran 8/10).
Konsep 1: TweenService, animasi halus
Tween mengubah properti sedikit demi sedikit dari nilai sekarang ke nilai tujuan. Contohnya pintu yang bergeser pelan, bukan langsung berpindah.
local TweenService = game:GetService("TweenService")
local info = TweenInfo.new(
2, -- durasi (detik)
Enum.EasingStyle.Quad, -- gaya gerakan
Enum.EasingDirection.Out, -- arah percepatan
0, -- jumlah ulang (-1 = selamanya)
false, -- bolak-balik?
0 -- jeda sebelum mulai
)
local tween = TweenService:Create(part, info, {
Position = Vector3.new(0, 10, 0),
Transparency = 0.5,
})
tween:Play()
tween.Completed:Wait() -- tunggu sampai selesai (opsional)
Tween bisa mengubah hampir semua properti berupa angka, Vector3, CFrame, Color3, atau UDim2, termasuk UI.
EasingStyle menentukan "rasa" gerakannya. Script 1 membuat arena balap yang menampilkan semua gaya sekaligus supaya kamu bisa membandingkan:
| Gaya | Rasa |
|---|---|
Linear |
Kecepatan tetap, terasa kaku seperti robot |
Quad / Sine |
Halus dan natural. Paling sering dipakai. |
Back |
Sedikit melewati tujuan lalu kembali, terasa "pop" |
Bounce |
Memantul seperti bola |
Elastic |
Bergetar seperti per |
Catatan: pemain yang berdiri di atas Part yang di-tween akan tergelincir, tidak ikut terbawa, karena tween memindahkan Part tanpa fisika. Untuk platform yang membawa pemain, gunakan constraint seperti
PrismaticConstraint(pelajari nanti) atau buat platform yang pendek jaraknya.
Konsep 2: Efek di server atau di client?
| Efek | Di mana | Alasan |
|---|---|---|
| Pintu terbuka, platform bergerak | Server | Semua pemain harus melihat posisi yang sama |
| Suara "cling" saat dapat koin, angka "+1" melayang | Client | Hanya untuk pemain itu, dan terasa instan tanpa jeda internet |
| Kembang api saat seseorang finish | Server | Biar semua pemain ikut melihat |
Konsep 3: Suara
local suara = Instance.new("Sound")
suara.SoundId = "rbxassetid://123456789" -- ID audio
suara.Volume = 0.5
suara.Parent = part -- di dalam Part = suara 3D (makin jauh makin pelan)
suara:Play()
- Sound di dalam Part menjadi suara 3D yang terdengar dari posisi Part itu.
- Sound di SoundService atau di client terdengar sama keras di mana pun, cocok untuk suara UI.
Mencari ID suara: di Studio buka Toolbox → Audio (atau Creator Store di web), cari misalnya "coin", "checkpoint", atau "success", lalu klik kanan → Copy Asset ID. Gunakan audio yang memang tersedia untuk umum. Audio yang tidak boleh dipakai di game-mu tidak akan berbunyi.
Konsep 4: Partikel
ParticleEmitter memancarkan banyak gambar kecil seperti percikan, asap, atau bintang.
emitter.Rate = 0laluemitter:Emit(40)memancarkan 40 partikel sekali saja (burst), cocok untuk efek "selamat!".emitter.Rate = 20memancarkan terus-menerus, cocok untuk api atau asap.
Konsep 5: Badge
Badge adalah pencapaian yang muncul di profil Roblox pemain. Badge membuat pemain merasa bangga dan sering membuat mereka kembali bermain.
- Buka create.roblox.com → Creations → game-mu → Engagement → Badges → Create a Badge.
- Isi nama, deskripsi, dan gambar. Biasanya ada beberapa badge gratis per hari, dan lebih dari itu dikenakan biaya Robux. Cek informasinya di halaman pembuatan badge.
- Salin Badge ID, lalu masukkan ke tabel
BADGEdi script 3.
Badge hanya bisa diberikan dari server, memakai BadgeService:AwardBadge(userId, badgeId). Di Studio, pemberian badge tidak benar-benar tercatat di profil (hanya muncul pesan di Output). Untuk mengujinya sungguhan, mainkan game yang sudah dipublikasikan.
Langkah di Roblox Studio
Script 1: Tween
- Buat 3 Part di Workspace dengan nama
PlatformBergerak,PintuGeser(buat tinggi seperti pintu, misalnya6, 10, 1), danBalokBerdenyut. Centang Anchored di semuanya. - Pasang
1-tween.server.luaudi ServerScriptService, lalu tekan Play. - Kalau ada Part yang tidak dibuat, script hanya menampilkan peringatan dan bagian lain tetap jalan.
- Arena balap easing akan muncul otomatis di samping titik spawn.
Script 2: Efek koin
- Cari suara koin di Toolbox, salin ID-nya, lalu isi
SUARA_KOINdi bagian atas script. Kalau dikosongkan, efek tetap jalan tanpa suara. - Pasang sebagai LocalScript, tekan Play, lalu ambil koin. Akan muncul angka +1 yang melayang dan memudar.
Script 3: Efek checkpoint dan badge
- (Opsional) Buat 2 badge, misalnya "Selamat Datang" dan "Juara Obby", lalu isi ID-nya di tabel
BADGE. - (Opsional) Isi
SUARA_CHECKPOINT. - Pasang script, tekan Play, lalu injak checkpoint. Akan muncul ledakan partikel.
Kode
1-tween.server.luau
- Jenis: Script
- Letakkan di: ServerScriptService
- Butuh (opsional): Part "PlatformBergerak", "PintuGeser",
local Players = game:GetService("Players")
local TweenService = game:GetService("TweenService")
-- Ambil Part dari Workspace. Kalau tidak ada, beri peringatan saja,
-- supaya bagian lain script tetap berjalan.
local function ambilPart(nama)
local part = workspace:FindFirstChild(nama)
if not part then
warn(`Part "{nama}" tidak ditemukan, bagian ini dilewati`)
end
return part
end
-- ------------------------------------------------------------
-- A. Platform bolak-balik selamanya
-- ------------------------------------------------------------
local platform = ambilPart("PlatformBergerak")
if platform then
platform.Anchored = true
local info = TweenInfo.new(
3, -- 3 detik sekali jalan
Enum.EasingStyle.Sine,
Enum.EasingDirection.InOut, -- pelan di awal DAN akhir
-1, -- ulang selamanya
true -- bolak-balik
)
-- CFrame + Vector3 = geser 20 stud ke arah Z
local tujuan = {CFrame = platform.CFrame + Vector3.new(0, 0, 20)}
TweenService:Create(platform, info, tujuan):Play()
end
-- ------------------------------------------------------------
-- B. Pintu yang terbuka saat disentuh pemain, lalu menutup lagi
-- ------------------------------------------------------------
local pintu = ambilPart("PintuGeser")
if pintu then
pintu.Anchored = true
local posisiTutup = pintu.CFrame
-- Pintu naik setinggi tubuhnya sendiri (masuk ke "langit-langit")
local posisiBuka = pintu.CFrame + Vector3.new(0, pintu.Size.Y, 0)
local info = TweenInfo.new(0.6, Enum.EasingStyle.Quad, Enum.EasingDirection.Out)
local sedangBergerak = false -- debounce (Pelajaran 7)
pintu.Touched:Connect(function(bagian)
if sedangBergerak then
return
end
if not Players:GetPlayerFromCharacter(bagian.Parent) then
return
end
sedangBergerak = true
local tweenBuka = TweenService:Create(pintu, info, {CFrame = posisiBuka})
tweenBuka:Play()
tweenBuka.Completed:Wait() -- tunggu sampai benar-benar terbuka
task.wait(3)
local tweenTutup = TweenService:Create(pintu, info, {CFrame = posisiTutup})
tweenTutup:Play()
tweenTutup.Completed:Wait()
sedangBergerak = false
end)
end
-- ------------------------------------------------------------
-- C. Balok berdenyut: beberapa properti sekaligus
-- ------------------------------------------------------------
local balok = ambilPart("BalokBerdenyut")
if balok then
balok.Anchored = true
balok.Material = Enum.Material.Neon
local info = TweenInfo.new(1, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut, -1, true)
TweenService:Create(balok, info, {
Size = balok.Size * 1.3,
Color = Color3.fromRGB(0, 170, 255),
Transparency = 0.4,
}):Play()
end
-- ------------------------------------------------------------
-- D. Arena balap EasingStyle: bandingkan "rasa" setiap gaya
-- Dibuat otomatis, tidak perlu menyiapkan Part apa pun.
-- ------------------------------------------------------------
local GAYA = {
Enum.EasingStyle.Linear,
Enum.EasingStyle.Quad,
Enum.EasingStyle.Sine,
Enum.EasingStyle.Back,
Enum.EasingStyle.Bounce,
Enum.EasingStyle.Elastic,
}
local folderArena = Instance.new("Folder")
folderArena.Name = "ArenaEasing"
folderArena.Parent = workspace
for i, gaya in ipairs(GAYA) do
local posisiAwal = Vector3.new(-30, 3, -20 + i * 5)
local pelari = Instance.new("Part")
pelari.Name = gaya.Name -- nama Enum, misalnya "Bounce"
pelari.Size = Vector3.new(2, 2, 2)
pelari.Position = posisiAwal
pelari.Anchored = true
pelari.Color = Color3.fromHSV(i / #GAYA, 0.8, 1)
pelari.Parent = folderArena
-- Label nama gaya di atas balok
local billboard = Instance.new("BillboardGui")
billboard.Size = UDim2.fromOffset(120, 30)
billboard.StudsOffset = Vector3.new(0, 2.5, 0)
billboard.Parent = pelari
local label = Instance.new("TextLabel")
label.Size = UDim2.fromScale(1, 1)
label.BackgroundTransparency = 1
label.TextScaled = true
label.Font = Enum.Font.GothamBold
label.TextColor3 = Color3.new(1, 1, 1)
label.TextStrokeTransparency = 0 -- garis tepi hitam supaya terbaca
label.Text = gaya.Name
label.Parent = billboard
-- Semua bergerak 25 stud dalam 2 detik, beda gayanya saja
local info = TweenInfo.new(2, gaya, Enum.EasingDirection.Out, -1, true, 0.5)
TweenService:Create(pelari, info, {Position = posisiAwal + Vector3.new(-25, 0, 0)}):Play()
end
2-efek-koin.client.luau
- Jenis: LocalScript
- Letakkan di: StarterPlayer -> StarterPlayerScripts
- Butuh: leaderstats (Pelajaran 8/10)
local Players = game:GetService("Players")
local TweenService = game:GetService("TweenService")
-- Isi dengan ID audio dari Toolbox, contoh: "rbxassetid://1234567890"
-- Kosongkan ("") kalau belum punya; efek tetap jalan tanpa suara.
local SUARA_KOIN = ""
local player = Players.LocalPlayer
local playerGui = player:WaitForChild("PlayerGui")
local koin = player:WaitForChild("leaderstats"):WaitForChild("Koin")
-- ------------------------------------------------------------
-- Siapkan suara satu kali saja, lalu diputar berulang-ulang
-- ------------------------------------------------------------
local suara = nil
if SUARA_KOIN ~= "" then
suara = Instance.new("Sound")
suara.SoundId = SUARA_KOIN
suara.Volume = 0.6
suara.Parent = script -- Sound di client = terdengar tanpa efek 3D
end
-- ------------------------------------------------------------
-- Wadah UI untuk angka melayang
-- ------------------------------------------------------------
local screenGui = Instance.new("ScreenGui")
screenGui.Name = "EfekKoin"
screenGui.ResetOnSpawn = false
screenGui.Parent = playerGui
local INFO_NAIK = TweenInfo.new(0.9, Enum.EasingStyle.Quad, Enum.EasingDirection.Out)
local INFO_POP = TweenInfo.new(0.15, Enum.EasingStyle.Back, Enum.EasingDirection.Out)
local function tampilkanAngka(teks, warna)
local label = Instance.new("TextLabel")
label.AnchorPoint = Vector2.new(0.5, 0.5)
-- Muncul di dekat label koin (Pelajaran 9), sedikit acak supaya
-- angka yang muncul berurutan tidak saling menumpuk
label.Position = UDim2.fromScale(0.12 + math.random() * 0.04, 0.22)
label.Size = UDim2.fromScale(0.01, 0.01) -- mulai kecil, lalu "pop"
label.BackgroundTransparency = 1
label.Font = Enum.Font.GothamBlack
label.TextScaled = true
label.TextColor3 = warna
label.TextStrokeTransparency = 0.3
label.Text = teks
label.Parent = screenGui
-- 1. "Pop" membesar
TweenService:Create(label, INFO_POP, {Size = UDim2.fromScale(0.08, 0.06)}):Play()
-- 2. Melayang naik sambil memudar
local naik = TweenService:Create(label, INFO_NAIK, {
Position = label.Position - UDim2.fromScale(0, 0.08),
TextTransparency = 1,
TextStrokeTransparency = 1,
})
naik:Play()
-- 3. Hapus setelah selesai supaya tidak menumpuk di memori
naik.Completed:Connect(function()
label:Destroy()
end)
end
-- ------------------------------------------------------------
-- Pantau perubahan koin
-- ------------------------------------------------------------
local nilaiSebelumnya = koin.Value
koin.Changed:Connect(function(nilaiBaru)
local selisih = nilaiBaru - nilaiSebelumnya
nilaiSebelumnya = nilaiBaru
if selisih > 0 then
tampilkanAngka(`+{selisih}`, Color3.fromRGB(255, 215, 0))
if suara then
suara:Play()
end
elseif selisih < 0 then
-- Koin berkurang (belanja) -> angka merah
tampilkanAngka(`{selisih}`, Color3.fromRGB(255, 90, 90))
end
end)
3-efek-checkpoint-badge.server.luau
- Jenis: Script
- Letakkan di: ServerScriptService
- Butuh: leaderstats (Pelajaran 8/10), folder "Checkpoints" (Pelajaran 8)
local Players = game:GetService("Players")
local BadgeService = game:GetService("BadgeService")
-- ID badge dari Creator Dashboard. Biarkan 0 kalau belum dibuat.
local BADGE = {
SelamatDatang = 0, -- diberikan saat pertama kali masuk
JuaraObby = 0, -- diberikan saat mencapai checkpoint terakhir
}
-- ID suara (opsional), contoh: "rbxassetid://1234567890"
local SUARA_CHECKPOINT = ""
local folderCheckpoint = workspace:WaitForChild("Checkpoints")
-- ------------------------------------------------------------
-- BADGE
-- ------------------------------------------------------------
local function beriBadge(player, idBadge)
if idBadge == 0 then
return
end
-- Cek dulu: sudah punya? (permintaan internet -> pcall)
local berhasilCek, sudahPunya = pcall(function()
return BadgeService:UserHasBadgeAsync(player.UserId, idBadge)
end)
if not berhasilCek or sudahPunya then
return
end
local berhasil, hasil = pcall(function()
return BadgeService:AwardBadge(player.UserId, idBadge)
end)
if berhasil and hasil then
print(`🏅 {player.Name} mendapat badge {idBadge}`)
else
warn(`Gagal memberi badge {idBadge} ke {player.Name}: {hasil}`)
end
end
-- ------------------------------------------------------------
-- EFEK PARTIKEL + SUARA di checkpoint
-- Dijalankan di SERVER, jadi semua pemain di sekitar ikut melihat.
-- ------------------------------------------------------------
local function siapkanEfek(part)
-- Buat emitter sekali saja per checkpoint, lalu dipakai ulang
local emitter = part:FindFirstChild("EfekCheckpoint")
if emitter then
return emitter
end
emitter = Instance.new("ParticleEmitter")
emitter.Name = "EfekCheckpoint"
emitter.Rate = 0 -- hanya burst lewat :Emit()
emitter.Lifetime = NumberRange.new(0.6, 1.2)
emitter.Speed = NumberRange.new(12, 22)
emitter.SpreadAngle = Vector2.new(60, 60)
emitter.Acceleration = Vector3.new(0, -25, 0) -- jatuh seperti confetti
emitter.Size = NumberSequence.new(0.6, 0) -- mengecil sampai hilang
emitter.LightEmission = 0.8 -- sedikit bercahaya
emitter.Color = ColorSequence.new(
Color3.fromRGB(0, 220, 255),
Color3.fromRGB(255, 255, 120)
)
emitter.EmissionDirection = Enum.NormalId.Top
emitter.Parent = part
if SUARA_CHECKPOINT ~= "" then
local suara = Instance.new("Sound")
suara.Name = "SuaraCheckpoint"
suara.SoundId = SUARA_CHECKPOINT
suara.Volume = 0.8
suara.Parent = part -- di dalam Part -> suara 3D
end
return emitter
end
local function mainkanEfek(nomorStage)
local part = folderCheckpoint:FindFirstChild(tostring(nomorStage))
if not part then
return
end
siapkanEfek(part):Emit(40)
local suara = part:FindFirstChild("SuaraCheckpoint")
if suara then
suara:Play()
end
end
-- ------------------------------------------------------------
-- Pantau Stage setiap pemain
-- ------------------------------------------------------------
local function saatPemainMasuk(player)
beriBadge(player, BADGE.SelamatDatang)
-- Tunggu leaderstats maksimal 30 detik (DataStore bisa butuh waktu)
local leaderstats = player:WaitForChild("leaderstats", 30)
if not leaderstats then
return
end
local stage = leaderstats:WaitForChild("Stage")
local stageSebelumnya = stage.Value
stage.Changed:Connect(function(stageBaru)
-- Efek hanya saat NAIK, bukan saat data dimuat atau direset
if stageBaru > stageSebelumnya then
mainkanEfek(stageBaru)
local jumlahCheckpoint = #folderCheckpoint:GetChildren()
if stageBaru >= jumlahCheckpoint then
print(`🏆 {player.Name} menyelesaikan obby!`)
beriBadge(player, BADGE.JuaraObby)
end
end
stageSebelumnya = stageBaru
end)
end
Players.PlayerAdded:Connect(saatPemainMasuk)
for _, player in ipairs(Players:GetPlayers()) do
task.spawn(saatPemainMasuk, player)
end
Error yang sering muncul
| Masalah | Penyebab |
|---|---|
Unable to cast to Dictionary |
Argumen ketiga TweenService:Create harus table {Properti = nilai} |
TweenService:Create property named 'X' cannot be tweened |
Properti itu bukan angka/vektor/warna, misalnya Anchored atau Name |
| Suara tidak berbunyi | ID salah, audio tidak boleh dipakai di game-mu, atau Volume 0 |
Failed to load sound di Output |
Sama seperti di atas. Coba audio lain dari Toolbox. |
| Badge tidak muncul di profil | Normal di Studio. Uji di game yang sudah dipublikasikan. |
Latihan
- Koin melayang: buat semua koin bergerak naik-turun terus dengan tween (
RepeatCount = -1,Reverses = true), selain berputar. - Tombol membesar: di UI toko (Pelajaran 9), buat tombol sedikit membesar saat kursor di atasnya (
MouseEnter) dan kembali normal saat kursor pergi (MouseLeave). Petunjuk: tween propertiSize. - Musik latar: putar musik yang berulang (
Looped = true) dengan volume pelan untuk semua pemain. - Badge koin: berikan badge "Kolektor" saat pemain mencapai 100 koin.
Kunci jawaban
Coba kerjakan sendiri dulu sebelum membaca bagian ini.
Latihan 1: Koin melayang naik-turun
Di script koin (Pelajaran 8), di dalam function aturKoin, tambahkan di bagian awal:
local TweenService = game:GetService("TweenService")
local INFO_MELAYANG = TweenInfo.new(1.2, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut, -1, true)
local function aturKoin(koin)
-- ... kode yang sudah ada ...
TweenService:Create(koin, INFO_MELAYANG, {
Position = koin.Position + Vector3.new(0, 1.5, 0),
}):Play()
end
Taruh TweenService dan INFO_MELAYANG di atas script (di luar function), supaya tidak dibuat ulang untuk setiap koin.
Apakah bentrok dengan loop yang memutar koin? Tidak. Tween hanya mengubah Position, sedangkan loop putar mengubah rotasi dengan membaca koin.CFrame yang sudah berisi posisi terbaru dari tween. Keduanya bisa jalan bersamaan.
Alternatif tanpa tween, langsung di loop putar yang sudah ada (lebih ringan untuk ratusan koin):
local posisiAwal = {} -- [koin] = posisi asli, diisi di aturKoin: posisiAwal[koin] = koin.Position
while true do
local t = os.clock()
for koin, asal in pairs(posisiAwal) do
local naikTurun = math.sin(t * 2) * 0.75 -- -0.75 sampai 0.75
local putaran = CFrame.Angles(0, t * 2, 0)
koin.CFrame = CFrame.new(asal + Vector3.new(0, naikTurun, 0)) * putaran
end
task.wait()
end
Latihan 2: Tombol membesar saat disorot
Di 3-toko.client.luau, di dalam buatTombol, setelah tombol dibuat:
local TweenService = game:GetService("TweenService")
local INFO_HOVER = TweenInfo.new(0.12, Enum.EasingStyle.Quad, Enum.EasingDirection.Out)
local function pasangEfekHover(tombol)
local ukuranNormal = tombol.Size
local ukuranBesar = UDim2.fromScale(
ukuranNormal.X.Scale * 1.06,
ukuranNormal.Y.Scale * 1.06
)
tombol.MouseEnter:Connect(function()
TweenService:Create(tombol, INFO_HOVER, {Size = ukuranBesar}):Play()
end)
tombol.MouseLeave:Connect(function()
TweenService:Create(tombol, INFO_HOVER, {Size = ukuranNormal}):Play()
end)
end
Lalu panggil pasangEfekHover(tombol) di dalam buatTombol.
Catatan: di HP tidak ada kursor, jadi efek hover tidak terlihat. Itu tidak masalah, karena efek ini hanya hiasan. Jangan menyembunyikan informasi penting di balik hover.
Karena tombol berada di dalam UIListLayout, tombol yang membesar akan sedikit menggeser tombol lain. Kalau tidak suka, pakai UIScale di dalam tombol dan tween properti Scale-nya (1 menjadi 1.06), karena UIScale tidak memengaruhi susunan layout.
Latihan 3: Musik latar
Script di ServerScriptService:
local SoundService = game:GetService("SoundService")
local ID_MUSIK = "" -- isi dengan ID audio dari Toolbox
if ID_MUSIK ~= "" then
local musik = Instance.new("Sound")
musik.Name = "MusikLatar"
musik.SoundId = ID_MUSIK
musik.Looped = true
musik.Volume = 0.3 -- pelan, supaya tidak mengganggu efek suara
musik.Parent = SoundService -- di SoundService = terdengar sama di mana pun (bukan 3D)
musik:Play()
end
Fitur tambahan yang disukai pemain: tombol 🔇 untuk mematikan musik. Buat di client (LocalScript) dengan mengubah SoundService.MusikLatar.Volume menjadi 0. Perubahan di client hanya memengaruhi pemain itu sendiri, persis yang diinginkan.
Latihan 4: Badge Kolektor 100 koin
Di 3-efek-checkpoint-badge.server.luau, tambahkan ID di tabel BADGE:
local BADGE = {
SelamatDatang = 0,
JuaraObby = 0,
Kolektor = 0, -- ID badge baru
}
Lalu di dalam saatPemainMasuk, setelah mendapatkan leaderstats:
local koin = leaderstats:WaitForChild("Koin")
local sudahDiberi = false
local function cekKolektor()
if not sudahDiberi and koin.Value >= 100 then
sudahDiberi = true
beriBadge(player, BADGE.Kolektor)
end
end
cekKolektor() -- pemain lama yang sudah punya 100+ koin dari DataStore
koin.Changed:Connect(cekKolektor)
Kenapa ada sudahDiberi? Setelah koin mencapai 100, setiap koin tambahan memicu Changed lagi (101, 102, ...). Tanpa penanda ini, script akan bertanya ke server Roblox (UserHasBadgeAsync) setiap kali koin bertambah, padahal itu permintaan internet yang sia-sia.
Diskusi (0)
Kami menyimpan nama (opsional), komentar, dan hash teknis (bukan alamat IP asli) untuk mencegah spam. Cookie fungsional dipakai agar Anda dapat mengedit atau menghapus komentar sendiri dari browser ini. Untuk permintaan penghapusan, gunakan halaman kontak.
Belum ada komentar. Mulai diskusinya.