Your browser lacks required capabilities. Please upgrade it or switch to another to continue.
Loading…
This is a demo/tutorial for Dynamics Avatar Drawer (DAD): [[https://gitlab.com/PerplexedPeach/dynamic-avatar-drawer]]
Note that DAD version 1.27.x or newer is needed for Twine integration (serialization was made compatible).
Look at this [[guide|http://perplexedpeach.gitlab.io/dynamic-avatar-drawer/twine_guide.html]] while going through the demo.
This [[Twine sample guide|https://qjzhvmqlzvoo5lqnrvuhmg-on.drv.tw/UInv/Sample_Code.html#Main%20Menu]] should also be helpful.
Sometimes the page may load without "SETTINGS" on the menu on the left. Reload when this happens (still not sure why it happens...)
Download this html and "Import From File" in Twine to look at the code.
[[Let's start with creating a character|Character creation]]
Wrap character initialization in if to avoid reinitialization when entering this passage.
This makes randomly creating the character easier.
<<if !$PC>>\
<<set $PC = new da.Player({
name: "Demo",
// provide specific values here to override the default ones set
fem : 11,
sub : 2,
basedim : {
areolaSize : 14.923766816143496,
armThickness : 58.468958260259555,
armLength : 45,
breastSize : 9.974887892376682,
buttFullness : 13.019992984917572,
chinWidth : 63.335671694142405,
eyelashLength : 4,
eyeSize : 13.019992984917572,
faceFem : 40,
faceLength : 212.32549982462294,
faceWidth : 82.74465099964925,
hairLength : 37.03963521571379,
hairStyle : 4,
hairHue : 0,
hairSaturation: 19.081024202034374,
hairLightness : 11.224131883549632,
handSize : 118.9757979656261,
height : 163.65022421524662,
hipWidth : 110.85584005612066,
legFem : 39.95790950543669,
legFullness : 4.489652753419852,
legLength : 98.79340582251841,
lipSize : 18.85654156436338,
lowerMuscle : 22.448263767099263,
neckLength : 72.73237460540162,
neckWidth : 39.489652753419854,
penisSize : 50,
shoulderWidth : 64.28699551569507,
skin : -1.9291476674850934,
testicleSize : 60,
upperMuscle : 0,
vaginaSize : 40,
waistWidth : 102.32549982462294,
},
decorativeParts: [
da.Part.create(da.BeautyMark, {side: null}),
],
Mods: {
armRotation : 0.4089686098654681,
arousal : 0,
breastPerkiness : 3.805682216766048,
browBotCurl : -1.6771300448430502,
browTopCurl : 6.358744394618835,
browCloseness : 0.7910313901345294,
browHeight : 1.2502242152466359,
browLength : -5.408071748878924,
browSharpness : -1.0457399103139018,
browThickness : -1.9641255605381165,
browOutBias : 0.5614349775784753,
browTilt : 2.041255605381167,
cheekFullness : 0.20343739038933606,
chinLength : -3,
earlobeLength : -0.45106980007015096,
eyeBias : 2.6,
eyeCloseness : 28.573991031390136,
eyeBotBias : -3.3417040358744394,
eyeBotSize : 13.980269058295967,
eyeHeight : -6.9641255605381165,
eyeTilt : 6.977578475336323,
eyeTopSize : -1.3212556053811646,
eyeWidth : -0.8161434977578477,
eyelashBias : 5.826025955804981,
eyelashAngle : 0.42,
eyelidBias : -0.7962118554893021,
eyelidHeight : -1.555243774114346,
hairAccessoryHue : 202.03437390389334,
hairAccessorySaturation: 59,
hairAccessoryLightness : 43,
irisHeight : -1.275336322869956,
irisHue : 70.25650224215246,
irisSaturation : 27.55156950672646,
irisLightness : 67.73094170403587,
irisSize : 15.497757847533633,
jawJut : -0.1227639424763236,
limbalRingSize : 25.255605381165918,
lipBias : 28.062780269058294,
lipCupidsBow : -7.47264573991032,
lipCurl : -4.387443946188341,
lipHeight : 0.3318385650224229,
lipTopCurve : -17.856502242152466,
lipTopSize : -8.060986547085204,
lipBotSize : 25.38475336322871,
lipWidth : -153.5695067264574,
neckCurve : -6.531041739740441,
noseHeight : -4.030493273542602,
noseLength : 41.78654708520179,
noseRidgeHeight : 0.3318385650224229,
noseRoundness : 4.83695067264574,
noseWidth : 18.724663677130046,
nostrilSize : 10.165919282511211,
pupilSize : 13.468958260259559,
shoeHeight : 3,
skinHue : -0.2547085201793706,
skinSaturation : -4.846636771300448,
skinLightness : 4.337219730941705,
age : -0.02511210762331828,
},
clothes: [
da.Clothes.create(da.Bra, {fill: "black"}),
da.Clothes.create(da.Panties, {fill: "black"})
],
hairFill : da.getPattern("blue-black ombre"),
hairStroke: da.getPattern("blue-black ombre"),
})>>\
<</if>>\
<<set _statPool = 5>>\
Stats pool <span id="stat-pool">_statPool</span>\
<<numberpool "_statPool">>\
<<for _name, _desc range da.statLimits>>\
<<if ["age", "pregnancy", "sub"].includes(_name)>><<continue>><</if>>
<<print "<<numberbox \"$PC." + _name + "\" $PC[_name] _desc.low _desc.high 1>> _name">>
<</for>>
<<onchange>>
<<run drawToMenu($PC)>>
<<replace "#stat-pool">>_statPool<</replace>>
<</numberpool>>
\
<<set _infPool = 100000>>\
\
<<numberpool "_infPool">>\
Vitals
<<for _name, _desc range da.vitalLimits>>
<<print "<<numberslider \"$PC.vitals." + _name + "\" $PC.vitals[_name] _desc.low $PC.maxVitals[_name] 0.1>> _name">>
<</for>>
<<onchange>>
<<run drawToMenu($PC)>>
<</numberpool>>\
The default character has her hairFill overriden with a specific color (think of it as being dyed) to demonstrate how to do this. \
If you want to change hair color, click the button below to clear this override.
<<button "clear hair override">>
<<run $PC.hairFill = null; $PC.hairStroke = null;>>
<<run drawToMenu($PC)>>
<</button>>
When creating the number sliders inside for loops, you have to use {{{<<print>>}}} do early binding of the looping variable.
Base dimensions \
<div id="dim-sliders"></div>\
<<button "make sliders">>
<<replace "#dim-sliders">>\
<<numberpool "_infPool">>\
<<for _name, _desc range da.baseDimDesc.human>>
<<print "<<numberslider \"$PC.basedim." + _name + "\" $PC.basedim[_name] _desc.low _desc.high 0.1>> _name">>
<</for>>
<<onchange>>
<<run drawToMenu($PC)>>
<</numberpool>>\
<</replace>>
<</button>>
Modifiers \
<div id="mod-sliders"></div>\
<<button "make sliders">>
<<replace "#mod-sliders">>\
<<numberpool "_infPool">>\
<<for _name, _desc range da.modLimits>>
<<if _desc.low < 1000>>\
<<print "<<numberslider \"$PC.Mods." + _name + "\" $PC.Mods[_name] -300 300 1>> _name">>\
<<else>>\
<<print "<<numberslider \"$PC.Mods." + _name + "\" $PC.Mods[_name] _desc.low _desc.high 1>> _name">>\
<</if>>
<</for>>
<<onchange>>
<<run drawToMenu($PC)>>
<</numberpool>>\
<</replace>>
<</button>>
<<run drawToMenu($PC)>>
Randomly create character
<<set _fembias = 0.5>>\
<<numberslider "_fembias" _fembias -1 1 0.01>> female bias
<<set _randomness = 1.0>>\
<<numberslider "_randomness" _randomness 0 2 0.01>> randomness
<<button "new random character">><<set $PC = recreateRandomCharacter($PC, _fembias, _randomness)>><<run Engine.play(passage())>><</button>>
Import character (paste text from [[tester|http://perplexedpeach.gitlab.io/dynamic-avatar-drawer/dist/test.html]]'s Create Character > Create in Code)
<<textarea "_importText" "">>
<<button "import">><<set $PC = eval(_importText)>><<run Engine.play(passage())>><</button>>
Export character
<div id="export"></div>\
<<button "export">>
<<set _exportText = "da.deserialize(\'" + da.serialize($PC) + "\');">>
<<replace "#export">>\
<<textarea "_exportText" _exportText autofocus>>
<</replace>>
<</button>>
[[Configure viewer settings]]
There should be a SETTINGS item on the menu bar to the left. If it doesn't exist, try reopening the page. This contains some options for adjusting how the avatar is displayed, such as how large the drawing canvas is.
Note that you can scroll in the menu bar if you choose a canvas height greater than your screen height.
Look at this guide for changing where to place the canvas on the page (or where to place additional canvases): \
[[how to place canvas group|http://perplexedpeach.gitlab.io/dynamic-avatar-drawer/twine_guide.html#placing_canvas%20group]]
[[Animating transformations]]
"INVENTORY" should show up on the menu bar on the left. Go check out the inventory and come back.
[[Manipulating body parts]]
Take a look at [[creating transformations|https://perplexedpeach.gitlab.io/dynamic-avatar-drawer/release/2016/10/18/1.14.art_upgrade.html]] and [[chaining transformations|https://perplexedpeach.gitlab.io/dynamic-avatar-drawer/release/2017/03/09/1.15.transformations.html]] to read about transformations.
<<set _transformDuration = 3000>>\
<<numberslider "_transformDuration" _transformDuration 100 10000 100>> transform duration (ms)
Facial expressions are applied with {{{<Player>.applyExpression}}}
<<set _allExpressions = Object.keys(da.Expression)>>\
<<run _allExpressions.splice(_allExpressions.indexOf("create"), 1)>>\
<<listbox "_expression" autoselect>>
<<optionsfrom _allExpressions>>
<</listbox>>
<<set _expressionDegree = 1>>\
<<numberslider "_expressionDegree" _expressionDegree -2 2 0.1>> expression degree
<<button "transform">>\
<<set _transform = da.createTransformation($PC, function(completion) {
console.log(completion);
$PC.applyExpression(da.Expression.create(da.Expression[_expression], completion * _expressionDegree));
drawToMenu($PC);
}, {})>>\
<<run da.transformAndShow(_transform, _transformDuration)>>
<</button>>
Modify breast size (can be applied multiple times)
<<set _modifier = 5>>\
<<numberslider "_modifier" _modifier -5 10 1>> size modifier
<<button "transform">>\
<<set _transform = da.createTransformation($PC, function() {drawToMenu($PC);}, {basedim: {breastSize: _modifier}})>>
<<run da.transformAndShow(_transform, _transformDuration)>>
<</button>>
[[Managing inventory]] <<if def $PC>>[[Inventory]]<</if>><<if typeof $clothesPool === "undefined">><<set $clothesPool = []>><</if>>\
Wearing
<<for _i, _clothes range $PC.clothes>>
<<capture _clothes>>\
<<c inventory_item>><<print _clothes.constructor.name>><</c>>\
<<link "remove">>\
<<run $PC.removeClothing(_clothes)>>\
<<run $clothesPool.push(_clothes)>>\
<<run Engine.play(passage())>>\
<</link>>\
<</capture>>\
<</for>>
Discarded
<<for _i, _clothes range $clothesPool>>
<<capture _clothes>>\
<<c inventory_item>><<print _clothes.constructor.name>><</c>>\
<<link "wear">>\
<<run $PC.wearClothing(_clothes)>>\
<<run $clothesPool.splice($clothesPool.indexOf(_clothes), 1)>>\
<<run Engine.play(passage())>>\
<</link>>\
<</capture>>\
<</for>>
Whole outfits (2 different ways of adding them - one in a {{{<<script>>}}} block using JavaScript and the other as a sequence of {{{<<run>>}}} blocks using TwineScript to highlight the different ways you can access data)
<<link "tube top">>\
<<script>>\
var tubeTop = da.Clothes.create(da.TubeTopSleeves, {
fill: "hsla(335.5,43.0%,55.3%,1.00)",
armLoose: 0.5446511149933203,
chestCoverage: 0.31024560682355373,
waistCoverage: 0.7837837837837838,
});
State.variables.PC.wearClothing(tubeTop);
var lacedLeggings = da.Clothes.create(da.LacedLeggins);
State.variables.PC.wearClothing(lacedLeggings);
var belt = da.Clothes.create(da.SimpleBelt, {
beltCurve: -3.2000000000000015,
beltWidth: 3.257630253827976,
thickness: 1.0999999999999992,
waistCoverage: 0.941629842770527,
fill: da.getPattern("kimono flowers", 100),
});
State.variables.PC.wearClothing(belt);
var lipStick = da.Clothes.create(da.Lipstick, {
fill: "hsla(327.9,38.8%,50.0%,1.00)",
});
State.variables.PC.wearClothing(lipStick);
var nails = da.Clothes.create(da.Nails, {
fill: "hsla(327.9,38.8%,50.0%,1.00)",
});
State.variables.PC.wearClothing(nails);
var choker = da.Clothes.create(da.CrossedChoker, {
});
State.variables.PC.wearClothing(choker);
var chainNecklace = da.Clothes.create(da.SimpleChain, {
cleavageCoverage: 0.1523995478368103,
dash: 4.3999999999999995,
});
State.variables.PC.wearClothing(chainNecklace);
var earrings = da.Clothes.create(da.RhombEarrings, {
});
State.variables.PC.wearClothing(earrings);
<</script>>\
<<run Engine.play(passage())>>\
<</link>>\
<<link "BDSM">>\
<<set _sweater = da.Clothes.create(da.LongSleevedSweater, {
thickness : 0.5,
neckCoverage : 0.1,
stomachCoverage: 0.15,
fill : da.getPattern("fishnet", 50),
});>>
<<run $PC.wearClothing(_sweater)>>
<<set _heels = da.Clothes.create(da.LockedCoveredFancyStilettos, {
stilettoFill : "rgb(255,223,0)",
platformHeight: 3
});>>
<<run $PC.wearClothing(_heels)>>
/% here I get lazy copy pasting into TwineScript so have another JS block
<<script>>
var gag = da.Clothes.create(da.MediumRingGag, {spiderLegs: true});
State.variables.PC.wearClothing(gag);
var blindfold = da.Clothes.create(da.BlindFold);
State.variables.PC.wearClothing(blindfold);
var wristRestraint = da.Clothes.create(da.WristRestraints);
State.variables.PC.wearClothing(wristRestraint);
var anklet = da.Clothes.create(da.ChainAnklet, {startAlongLeg: 0.2, side: da.Part.LEFT});
State.variables.PC.wearClothing(anklet);
var ankleRestraint = da.Clothes.create(da.AnkleRestraints);
State.variables.PC.wearClothing(ankleRestraint);
<</script>>
<<run Engine.play(passage())>>\
<</link>>
You can contribute clothing templates by following the [[content creation guide|http://perplexedpeach.gitlab.io/dynamic-avatar-drawer/content_creation.html]]\
and looking at the code under `src/clothing`
Tattoos and items (unattached to a body part) can also be added and removed.\
Play around with it in the [[live tester|http://perplexedpeach.gitlab.io/dynamic-avatar-drawer/dist/test.html]]\
which also generates directly usable code.
A cleaner JS way of handling the inventory:
<div id="clothing_inv"></div>
<<run createClothingGUI($PC, "clothing_inv", $clothesPool)>>
[[Return|$return]]
Body parts can be easily removed or attached to the avatar and be visually represented (clothing should also be designed to handle missing drawpoints - such as sleeves when there is an arm but no hand). Try removing a body part.
<<for _i, _part range $PC.parts>>
<<capture _part>>\
<<c inventory_item>><<print _part.constructor.name>><</c>>\
<<link "remove">>
<<run $PC.removePart(_part.loc)>>\
<<run Engine.play(passage())>>\
<</link>>\
<</capture>>\
<</for>>
Facial parts and decorative parts are kept separate from structural body parts. Most methods involving part will take the part array as an optional last argument (by default it's the structural parts). Play around with it in the [[live tester|http://perplexedpeach.gitlab.io/dynamic-avatar-drawer/dist/test.html]]
[[Finishing up]]Now that you got this far you should be ready to incorporate DAD into your Twine game!
Take a look at [[usage guide|http://perplexedpeach.gitlab.io/dynamic-avatar-drawer/usage.1.0.html]] for general usage tips. For creating your own visuals (Part templates), look at the [[content creation guide|http://perplexedpeach.gitlab.io/dynamic-avatar-drawer/content_creation.html]].