تبدیل Adobe Illustrator به یک تولیدکننده مردم (معروف به عروسک ساز لباس)
مشکل
اخیراً یک سوال بسیار سخت داشتم: پر کردن یک تصویر با صدها شخصیت منحصر به فرد در کمتر از 2 هفته. این به طور طبیعی یک دستور بسیار بلند است، بنابراین من جاوا اسکریپت را شکستم.
من هرگز از جاوا اسکریپت بر روی هیچ چیز دیگری غیر از افتر افکت استفاده نکرده ام، اما می دانستم که شما می توانید اسکریپت ها را بر روی سایر برنامه های ادوبی بنویسید و اجرا کنید. من راهنمای اسکریپت را باز کردم و فهمیدم که چیزی که میخواهم خیلی درخواستی نیست.
تنها کاری که باید انجام می دادم این بود که بخش هایی از بدن را ترسیم کنم که قابل تعویض بودند. من قبلاً با این ایده بازی کرده ام، در نوجوانی ام با استفاده از فلش عروسک های آرایشی درست کرده ام و اخیراً با picrew بازی کرده ام. بنابراین من باید یک اسکریپت بنویسم که به اندازه کافی هوشمند باشد که بگوید “در هر لایه، تمام لایه های فرعی را خاموش کنید، سپس یکی را به طور تصادفی دوباره روشن کنید.” اگر بتوانم رنگ پوست و لباسها را هم به صورت تصادفی دربیاورم، امتیاز پاداش.
در اینجا نحوه رسیدن من به این مهم است.
روشن کردن یک لایه فرعی تصادفی
شروع کردم به آزمایش ایده ام روی اشکال. میدانستم اگر بتوانم یک اسکریپت را برای 1 لایه کار کنم، میتوانم آن را برای چند لایه کار کنم. یک سند با 1 لایه و 4 لایه فرعی تنظیم کردم و روی هر کدام یک شکل ساده کشیدم: مربع، دایره، ستاره و مثلث.
من تصمیم گرفتم از لایههای فرعی به جای گروهها/ آیتمهای مسیر آزاد/ مسیرهای مرکب استفاده کنم، زیرا به این معنی بود که میتوانم آثار هنری را مرتب نگه دارم و کد من میتوانست ثابت بماند.
من adobe ExtendScript CC را باز کردم. من دوست دارم از این استفاده کنم زیرا می تواند بدون نیاز به ذخیره فایل jsx. مستقیماً به illustrator متصل شود.
اولین کاری که باید انجام می دادم این بود که به اسکریپت بگویم تمام لایه های فرعی را بچرخاند خاموش. این برای اطمینان از این است که وقتی اسکریپت چندین بار اجرا می شود، هیچ لایه فرعی باقی نمی ماند. من در نهایت یک تابع با استفاده از حلقههای for برای انجام این کار ایجاد کردم:
var doc = app.activeDocument;
function hideAll() {
for (i = 0; i < doc.layers.length; i++) {
for (a = 0; a < doc.layers[i].layers.length; a++) {
doc.layers[i].layers[a].visible = false;
}
}
};
hideAll();
در اینجا، من یک متغیر برای سند فعال خود تنظیم کردم تا از تایپ مکرر “app.activeDocument” در طول اسکریپت خودداری کنم. سپس تابع خود “hideAll” را ایجاد کردم. اولین حلقه for در هر لایه در سند به نوبه خود چرخه می زند، سپس زمانی که در همه آنها حلقه زد متوقف می شود (با استفاده از آرگومان “i < doc.layers.length" برای تنظیم تعداد لایه ها، و "i++" به صورت افزایشی 1 را به مقدار i اضافه کنید تا به حداکثر تعداد لایه برسیم). حلقه دوم در تمام لایه های فرعی در هر لایه حلقه می زند، سپس زمانی که در همه آنها حلقه زده است متوقف می شود (ارجاع به لایه های فرعی در illustrator به سادگی نیاز به استفاده مکرر از "لایه ها دارد.[index]"، به همین دلیل است که من این گردش کار را انتخاب کردم. همانطور که حلقه از هر زیر لایه عبور می کند، دید لایه فرعی را روی "false" تنظیم می کند و آن را خاموش می کند.
بعد من نیاز به چرخش اسکریپت داشتم بر یک لایه فرعی تصادفی فکر کردم تمام چیزی که نیاز داشتم یک متغیر تصادفی بود و برای تنظیم نمایان بودن لایه فرعی روی true. بنابراین من یک تابع جدید ایجاد کردم:
function showRandom() {
for (i = 0; i < doc.layers.length; i++) {
var randomLayerCount = Math.floor(Math.random() * doc.layers[i].layers.length);
doc.layers[i].layers[randomLayerCount].visible = true;
}
};
showRandom از هر لایه در سند عبور می کند و یک عدد تصادفی برای متغیر “randomLayerCount” بین 0 و تعداد زیر لایه های لایه فعلی ایجاد می کند. از “Math.random” برای تصادفی کردن یک عدد بین 0 و 1 استفاده می کند، آن عدد را در تعداد لایه های فرعی ضرب می کند و در نهایت از “Math.floor” برای گرد کردن آن عدد به یک عدد صحیح استفاده می کند. سپس حلقه for زیر لایه ای را روشن می کند که عدد شاخص آن با عدد تولید شده به طور تصادفی مطابقت دارد. سپس هر دو تابع را با هم اجرا می کنم:
hideAll();
showRandom();
برای روشن کردن تصادفی یکی از لایه های فرعی شکل.
رنگ تصادفی
کار می کند! حالا به رنگ ها فکر کنیم. اگر قرار است مولد مردم واقعاً کار کند، باید بتواند رنگ پوست، رنگ مو، رنگ لباس و غیره را تغییر دهد.
در نهایت من به جای ارجاع به نمونهها، رنگها را درون اسکریپت ایجاد کردم، در صورتی که میخواستم اسکریپت را برای سند دیگری تغییر دهم. من رنگ هایم را اینگونه ساختم:
var colour01 = new CMYKColor();
colour01.cyan = 3;
colour01.magenta = 11;
colour01.yellow = 18;
colour01.black = 0;
از آنجایی که سند من باید برای چاپ باشد، از CMYK به جای RGB استفاده کردم (اگر نیاز به استفاده از RGB دارید، فقط از “new RGBColor()” استفاده کنید و هر مقدار رنگ RGB را تنظیم کنید). من مقادیر رنگ جدیدم را تعیین کردم و این کار را برای هر رنگی که باید به اسکریپت اضافه کنم انجام دادم.
سپس یک آرایه برای تمام رنگهایی که میخواستم چرخه بزنم ایجاد کردم. در این مورد، من میخواستم 4 گزینه مختلف رنگ پوست را بررسی کنم:
var skinTones = [colour01, colour02, colour03, colour04];
و یک عدد تصادفی که می تواند برای انتخاب هر رنگ استفاده شود:
var skinRandom = Math.floor(Math.random() * skinTones.length);
حالا اشکال را رنگ کنید. تصمیم گرفتم که میخواهم همه شکلها را همزمان رنگ کنم، اگر بخواهم بعد از اجرای اسکریپت تصویر را تنظیم کنم. این به این معنی بود که باید برگردم بر تمام لایه های فرعی، به منظور تبدیل آنها به اهداف معتبر برای انتخاب اسکریپت. من یک تابع جدید ایجاد کردم، برعکس hideAll:
function showAll() {
for (i = 0; i < doc.layers.length; i++) {
for (a = 0; a < doc.layers[i].layers.length; a++) {
doc.layers[i].layers[a].visible = true;
}
}
};
حالا همه لایههای فرعی روشن بودند، من میتوانستم تمام اشکال را به ترتیب مرور کنم و رنگهایشان را با یک تابع جدید تغییر دهم:
function colourAll() {
for (i = 0; i < doc.pathItems.length; i++) {
doc.pathItems[i].fillColor = skinTones[skinRandom];
}
};
این تابع از تمام pathItem های سند عبور می کند و رنگ آنها را تغییر می دهد. من در این مرحله تصمیم گرفتم فقط از pathItems در سند تصویرگرم برای کارهای هنری نهایی استفاده کنم. pathItem ها به طور خاص چیست؟ آنها هر شکلی هستند که هنگام ترسیم در illustrator به عنوان “مسیر” نشان داده می شوند:
این بدان معناست که باید به خاطر داشته باشم که هنگام ترسیم قطعات برای افرادم که میخواستم اسکریپت بعداً رنگآمیزی کند (وگرنه اسکریپت کار نمیکند) از مسیرهای مرکب، الگوها یا هر چیز پیچیدهای استفاده نکنم.
من توابع جدید خود را به انتهای اسکریپت اضافه می کنم:
showAll();
colourAll();
hideAll();
showRandom();
اکنون اسکریپت همه لایههای فرعی را روشن میکند، همه pathItems را به رنگ تصادفی از آرایه رنگ میکند، همه لایههای فرعی را خاموش میکند، سپس در نهایت 1 را بهصورت تصادفی انتخاب میکند تا دوباره روشن شود. این ممکن است بیش از حد مسائل را پیچیده کند، اما متوجه شدم که برای من، این منطقی ترین معنا را دارد.
ساختن مردم مولد
از اینجا، بلوکهای اولیهای را که برای ساختن نیروگاهم نیاز داشتم، داشتم. اکنون باید فکر کنم که چگونه این کار برای چندین تغییر رنگ و چندین لایه کار می کند. من شکلهای بیشتری را بهعنوان مکانی برای نگهداری آثار هنری تمامشدهام، در چندین لایه و لایههای فرعی ایجاد کردم.
بعد من در مجموع 12 رنگ ایجاد کردم (به 4 رنگی که قبلاً ایجاد شده اضافه می شود) تا رنگ پوست، مو و لباس من باشد. سپس آرایه ها و مولدهای اعداد تصادفی را ایجاد می کنم، مانند:
var skinTones = [colour01, colour02, colour03, colour04];
var skinRandom = Math.floor(Math.random() * skinTones.length);
var clothesTones = [colour05, colour06, colour07, colour08];
var clothesRandom01 = Math.floor(Math.random() * clothesTones.length);
var clothesRandom02 = Math.floor(Math.random() * clothesTones.length);
var clothesRandom03 = Math.floor(Math.random() * clothesTones.length);
var hairTones = [colour09, colour10, colour11, colour12];
var hairRandom = Math.floor(Math.random() * hairTones.length);
من به لباسها 3 عدد مولد اعداد تصادفی جداگانه دادم، بنابراین لایههای پیراهن، شلوار و کفش میتوانند رنگهای متفاوتی داشته باشند. اکنون که بیش از 1 آرایه رنگی داشتم، باید به عقب برگردم و تابع “colourAll” خود را تغییر دهم. تصمیم گرفتم این را به چند عملکرد تقسیم کنم: “colourSkin”، “colourHair”، “colourClothes01″، “colourClothes02” و “colourClothes03”. بعد از این، من به راهی نیاز داشتم تا مشخص کنم کدام مسیر باید با رنگ پوست، کدام لباس و کدام مو باید رنگ شود. تصمیم گرفتم این کار را با نامگذاری pathItems خود در illustrator انجام دهم.
سپس یک دستور if را به توابع جدید خود اضافه کردم، به طوری که آنها فقط pathItem هایی را که نام آنها با تابع مطابقت دارد، دوباره رنگ می کنند:
function colourSkin() {
for (i = 0; i < doc.pathItems.length; i++) {
if (doc.pathItems[i].name == "skin") doc.pathItems[i].fillColor = skinTones[skinRandom];
}
};
function colourHair() {
for (i = 0; i < doc.pathItems.length; i++) {
if (doc.pathItems[i].name == "hair") doc.pathItems[i].fillColor = hairTones[hairRandom];
}
};
function colourClothes01() {
for (i = 0; i < doc.pathItems.length; i++) {
if (doc.pathItems[i].name == "clothes01") doc.pathItems[i].fillColor = clothesTones[clothesRandom01];
}
};
function colourClothes02() {
for (i = 0; i < doc.pathItems.length; i++) {
if (doc.pathItems[i].name == "clothes02") doc.pathItems[i].fillColor = clothesTones[clothesRandom02];
}
};
function colourClothes03() {
for (i = 0; i < doc.pathItems.length; i++) {
if (doc.pathItems[i].name == "clothes03") doc.pathItems[i].fillColor = clothesTones[clothesRandom03];
}
};
سپس توابع انتهای اسکریپت را دوباره به روز کرد:
showAll();
colourSkin();
colourHair();
colourClothes01();
colourClothes02();
colourClothes03();
hideAll();
showRandom();
من تقریباً آنجا بودم! من فیلمنامه ای داشتم که می توانست رنگ ها را آن طور که می خواستم تغییر دهد. اما اگر میخواستم لایههای دستها، پاها و موهایم را جدا کنم (که برای همپوشانی درست آثار هنری به آن نیاز دارم) باید یک تغییر کوچک در عملکرد showRandom خود ایجاد کنم. در غیر این صورت، هر لایه بازو، مو و پا به طور تصادفی بهصورت تصادفی انتخاب میشود، به این معنی که طول آستینها با هم مطابقت ندارند و آثار هنری به هم میخورد!
مانند رنگها، تابع را به چندین تابع تقسیم کردم و از نام لایههایم برای برچسب زدن همه چیز استفاده کردم. اگر اثر هنری یک لایه میتوانست مستقل باشد و به همان عدد تصادفی دیگری نیاز نداشته باشد، نام آن را “واحد” گذاشتم. اگر لازم بود لایهای با همان عدد تصادفی با لایه دیگر مطابقت داشته باشد، به آن و همه لایههای دیگری که با آن مطابقت دارند، یک نام ثابت مانند “بازو”، “پا” یا “مو” میگذارم و مطمئن میشوم که هر یک تعداد یکسان دارند. از لایه های فرعی، به همان ترتیبی که می خواستم نمایش داده شوند.
سپس، به جای اینکه متغیر تصادفی در داخل حلقه for برای این توابع باشد، آن را ایجاد کردم خارج از از عملکرد به طور کامل:
function showUnit() {
for (i = 0; i < doc.layers.length; i++) {
var randomUnitCount = Math.floor(Math.random() * doc.layers[i].layers.length);
if (doc.layers[i].name == "unit") doc.layers[i].layers[randomUnitCount].visible = true;
}
};
var randomHairCount = Math.floor(Math.random() * doc.layers["hair"].layers.length);
function showHair() {
for (i = 0; i < doc.layers.length; i++) {
if (doc.layers[i].name == "hair") doc.layers[i].layers[randomHairCount].visible = true;
}
};
var randomLegCount = Math.floor(Math.random() * doc.layers["leg"].layers.length);
function showLeg() {
for (i = 0; i < doc.layers.length; i++) {
if (doc.layers[i].name == "leg") doc.layers[i].layers[randomLegCount].visible = true;
}
};
var randomArmCount = Math.floor(Math.random() * doc.layers["arm"].layers.length);
function showArm() {
for (i = 0; i < doc.layers.length; i++) {
if (doc.layers[i].name == "arm") doc.layers[i].layers[randomArmCount].visible = true;
}
};
این بدان معناست که هر بار که از حلقه for استفاده میشود، عدد تصادفی دوباره پخش نمیشود. یک بار دیگر، من توابع خود را در پایان اسکریپت به روز کردم:
showAll();
colourSkin();
colourHair();
colourClothes01();
colourClothes02();
colourClothes03();
hideAll();
showUnit();
showHair();
showLeg();
showArm();
و تمام شد! اسکریپت اصلی برای تصادفی سازی اعضای بدن در ایلوستریتور.
از اینکه در تمام این فرآیند با من بودید متشکرم! امیدوارم این تفکیک مفید بوده باشد. لطفاً اگر سؤالی دارید یا راه هایی برای بهبود فرآیند دارید، نظر بدهید.
فیلمنامه کامل:
var doc = app.activeDocument;
//colours
var colour01 = new CMYKColor();
colour01.cyan = 3;
colour01.magenta = 11;
colour01.yellow = 18;
colour01.black = 0;
var colour02 = new CMYKColor();
colour02.cyan = 9;
colour02.magenta = 29;
colour02.yellow = 46;
colour02.black = 0;
var colour03 = new CMYKColor();
colour03.cyan = 30;
colour03.magenta = 46;
colour03.yellow = 53;
colour03.black = 4;
var colour04 = new CMYKColor();
colour04.cyan = 37;
colour04.magenta = 77;
colour04.yellow = 100;
colour04.black = 46;
var colour05 = new CMYKColor();
colour05.cyan = 63;
colour05.magenta = 22;
colour05.yellow = 0;
colour05.black = 0;
var colour06 = new CMYKColor();
colour06.cyan = 55;
colour06.magenta = 0;
colour06.yellow = 96;
colour06.black = 0;
var colour07 = new CMYKColor();
colour07.cyan = 0;
colour07.magenta = 51;
colour07.yellow = 96;
colour07.black = 0;
var colour08 = new CMYKColor();
colour08.cyan = 0;
colour08.magenta = 96;
colour08.yellow = 89;
colour08.black = 0;
var colour09 = new CMYKColor();
colour09.cyan = 36;
colour09.magenta = 57;
colour09.yellow = 84;
colour09.black = 23;
var colour10 = new CMYKColor();
colour10.cyan = 5;
colour10.magenta = 0;
colour10.yellow = 93;
colour10.black = 0;
var colour11 = new CMYKColor();
colour11.cyan = 75;
colour11.magenta = 68;
colour11.yellow =67;
colour11.black = 90;
var colour12 = new CMYKColor();
colour12.cyan = 0;
colour12.magenta = 0;
colour12.yellow = 0;
colour12.black = 0;
var skinTones = [colour01, colour02, colour03, colour04];
var skinRandom = Math.floor(Math.random() * skinTones.length);
var clothesTones = [colour05, colour06, colour07, colour08];
var clothesRandom01 = Math.floor(Math.random() * clothesTones.length);
var clothesRandom02 = Math.floor(Math.random() * clothesTones.length);
var clothesRandom03 = Math.floor(Math.random() * clothesTones.length);
var hairTones = [colour09, colour10, colour11, colour12];
var hairRandom = Math.floor(Math.random() * hairTones.length);
//functions
function showAll() {
for (i = 0; i < doc.layers.length; i++) {
for (a = 0; a < doc.layers[i].layers.length; a++) {
doc.layers[i].layers[a].visible = true;
}
}
};
function colourSkin() {
for (i = 0; i < doc.pathItems.length; i++) {
if (doc.pathItems[i].name == "skin") doc.pathItems[i].fillColor = skinTones[skinRandom];
}
};
function colourHair() {
for (i = 0; i < doc.pathItems.length; i++) {
if (doc.pathItems[i].name == "hair") doc.pathItems[i].fillColor = hairTones[hairRandom];
}
};
function colourClothes01() {
for (i = 0; i < doc.pathItems.length; i++) {
if (doc.pathItems[i].name == "clothes01") doc.pathItems[i].fillColor = clothesTones[clothesRandom01];
}
};
function colourClothes02() {
for (i = 0; i < doc.pathItems.length; i++) {
if (doc.pathItems[i].name == "clothes02") doc.pathItems[i].fillColor = clothesTones[clothesRandom02];
}
};
function colourClothes03() {
for (i = 0; i < doc.pathItems.length; i++) {
if (doc.pathItems[i].name == "clothes03") doc.pathItems[i].fillColor = clothesTones[clothesRandom03];
}
};
function hideAll() {
for (i = 0; i < doc.layers.length; i++) {
for (a = 0; a < doc.layers[i].layers.length; a++) {
doc.layers[i].layers[a].visible = false;
}
}
};
function showUnit() {
for (i = 0; i < doc.layers.length; i++) {
var randomUnitCount = Math.floor(Math.random() * doc.layers[i].layers.length);
if (doc.layers[i].name == "unit") doc.layers[i].layers[randomUnitCount].visible = true;
}
};
var randomHairCount = Math.floor(Math.random() * doc.layers["hair"].layers.length);
function showHair() {
for (i = 0; i < doc.layers.length; i++) {
if (doc.layers[i].name == "hair") doc.layers[i].layers[randomHairCount].visible = true;
}
};
var randomLegCount = Math.floor(Math.random() * doc.layers["leg"].layers.length);
function showLeg() {
for (i = 0; i < doc.layers.length; i++) {
if (doc.layers[i].name == "leg") doc.layers[i].layers[randomLegCount].visible = true;
}
};
var randomArmCount = Math.floor(Math.random() * doc.layers["arm"].layers.length);
function showArm() {
for (i = 0; i < doc.layers.length; i++) {
if (doc.layers[i].name == "arm") doc.layers[i].layers[randomArmCount].visible = true;
}
};
//run
showAll();
colourSkin();
colourHair();
colourClothes01();
colourClothes02();
colourClothes03();
hideAll();
showUnit();
showHair();
showLeg();
showArm();